From 58cf31b50699ed9f523de38c8e943f3bbd1ced9e Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 6 Mar 2015 17:23:53 -0800 Subject: [PATCH 001/123] Make pthread join_state not protected by g_thread_list_lock. 1. Move the representation of thread join_state from pthread.attr.flag to pthread.join_state. This clarifies thread state change. 2. Use atomic operations for pthread.join_state. So we don't need to protect it by g_thread_list_lock. g_thread_list_lock will be reduced to only protect g_thread_list or even removed in further changes. Bug: 19636317 Change-Id: I31fb143a7c69508c7287307dd3b0776993ec0f43 --- libc/bionic/pthread_attr.cpp | 5 ++++ libc/bionic/pthread_create.cpp | 8 +++++- libc/bionic/pthread_detach.cpp | 21 +++++++-------- libc/bionic/pthread_exit.cpp | 48 +++++++++++++++++----------------- libc/bionic/pthread_internal.h | 10 +++++++ libc/bionic/pthread_join.cpp | 13 +++++---- 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp index be1c25228..7ad34311e 100644 --- a/libc/bionic/pthread_attr.cpp +++ b/libc/bionic/pthread_attr.cpp @@ -170,6 +170,11 @@ int pthread_attr_getguardsize(const pthread_attr_t* attr, size_t* guard_size) { int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) { pthread_internal_t* thread = reinterpret_cast(t); *attr = thread->attr; + // We prefer reading join_state here to setting thread->attr.flags in pthread_detach. + // Because data race exists in the latter case. + if (atomic_load(&thread->join_state) == THREAD_DETACHED) { + attr->flags |= PTHREAD_ATTR_FLAG_DETACHED; + } // The main thread's stack information is not stored in thread->attr, and we need to // collect that at runtime. if (thread->tid == getpid()) { diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 2bca43f83..a4bd054c7 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -86,6 +86,12 @@ void __init_alternate_signal_stack(pthread_internal_t* thread) { int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) { int error = 0; + if (__predict_true((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) == 0)) { + atomic_init(&thread->join_state, THREAD_NOT_JOINED); + } else { + atomic_init(&thread->join_state, THREAD_DETACHED); + } + // Set the scheduling policy/priority of the thread. if (thread->attr.sched_policy != SCHED_NORMAL) { sched_param param; @@ -263,7 +269,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, if (init_errno != 0) { // Mark the thread detached and replace its start_routine with a no-op. // Letting the thread run is the easiest way to clean up its resources. - thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED; + atomic_store(&thread->join_state, THREAD_DETACHED); thread->start_routine = __do_nothing; pthread_mutex_unlock(&thread->startup_handshake_mutex); return init_errno; diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index c80066008..0712d0d67 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -38,21 +38,18 @@ int pthread_detach(pthread_t t) { return ESRCH; } - if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) { - return EINVAL; // Already detached. + ThreadJoinState old_state = THREAD_NOT_JOINED; + while (old_state == THREAD_NOT_JOINED && + !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_DETACHED)) { } - - if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) { - return 0; // Already being joined; silently do nothing, like glibc. - } - - // If the thread has not exited, we can detach it safely. - if ((thread->attr.flags & PTHREAD_ATTR_FLAG_ZOMBIE) == 0) { - thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED; - return 0; + switch (old_state) { + case THREAD_NOT_JOINED: return 0; + case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc. + case THREAD_DETACHED: return THREAD_DETACHED; + case THREAD_EXITED_NOT_JOINED: // Call pthread_join out of scope of pthread_accessor. } } - // The thread is in zombie state, use pthread_join to clean it up. + // The thread is in THREAD_EXITED_NOT_JOINED, use pthread_join to clean it up. return pthread_join(t, NULL); } diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index d0d64b0ec..81cc67be9 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -87,9 +87,12 @@ void pthread_exit(void* return_value) { thread->alternate_signal_stack = NULL; } - bool free_mapped_space = false; - pthread_mutex_lock(&g_thread_list_lock); - if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) { + ThreadJoinState old_state = THREAD_NOT_JOINED; + while (old_state == THREAD_NOT_JOINED && + !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) { + } + + if (old_state == THREAD_DETACHED) { // The thread is detached, no one will use pthread_internal_t after pthread_exit. // So we can free mapped space, which includes pthread_internal_t and thread stack. // First make sure that the kernel does not try to clear the tid field @@ -97,28 +100,25 @@ void pthread_exit(void* return_value) { __set_tid_address(NULL); // pthread_internal_t is freed below with stack, not here. + pthread_mutex_lock(&g_thread_list_lock); _pthread_internal_remove_locked(thread, false); - free_mapped_space = true; - } else { - // Mark the thread as exiting without freeing pthread_internal_t. - thread->attr.flags |= PTHREAD_ATTR_FLAG_ZOMBIE; + pthread_mutex_unlock(&g_thread_list_lock); + + if (thread->mmap_size != 0) { + // We need to free mapped space for detached threads when they exit. + // That's not something we can do in C. + + // We don't want to take a signal after we've unmapped the stack. + // That's one last thing we can handle in C. + sigset_t mask; + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + + _exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size); + } } - pthread_mutex_unlock(&g_thread_list_lock); - if (free_mapped_space && thread->mmap_size != 0) { - // We need to free mapped space for detached threads when they exit. - // That's not something we can do in C. - - // We don't want to take a signal after we've unmapped the stack. - // That's one last thing we can handle in C. - sigset_t mask; - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, NULL); - - _exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size); - } else { - // No need to free mapped space. Either there was no space mapped, or it is left for - // the pthread_join caller to clean up. - __exit(0); - } + // No need to free mapped space. Either there was no space mapped, or it is left for + // the pthread_join caller to clean up. + __exit(0); } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 6ace30180..8da99dd36 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -29,6 +29,7 @@ #define _PTHREAD_INTERNAL_H_ #include +#include #include "private/bionic_tls.h" @@ -46,6 +47,13 @@ struct pthread_key_data_t { void* data; }; +enum ThreadJoinState { + THREAD_NOT_JOINED, + THREAD_EXITED_NOT_JOINED, + THREAD_JOINED, + THREAD_DETACHED +}; + struct pthread_internal_t { struct pthread_internal_t* next; struct pthread_internal_t* prev; @@ -74,6 +82,8 @@ struct pthread_internal_t { pthread_attr_t attr; + _Atomic(ThreadJoinState) join_state; + __pthread_cleanup_t* cleanup_stack; void* (*start_routine)(void*); diff --git a/libc/bionic/pthread_join.cpp b/libc/bionic/pthread_join.cpp index e3350efd3..15543b48e 100644 --- a/libc/bionic/pthread_join.cpp +++ b/libc/bionic/pthread_join.cpp @@ -44,16 +44,15 @@ int pthread_join(pthread_t t, void** return_value) { return ESRCH; } - if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) { + ThreadJoinState old_state = THREAD_NOT_JOINED; + while ((old_state == THREAD_NOT_JOINED || old_state == THREAD_EXITED_NOT_JOINED) && + !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_JOINED)) { + } + + if (old_state == THREAD_DETACHED || old_state == THREAD_JOINED) { return EINVAL; } - if ((thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) != 0) { - return EINVAL; - } - - // Okay, looks like we can signal our intention to join. - thread->attr.flags |= PTHREAD_ATTR_FLAG_JOINED; tid = thread->tid; tid_ptr = &thread->tid; } From 5b8e7cd957f9380e93c3aee84962d157fe0bc526 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 4 Mar 2015 17:36:59 -0800 Subject: [PATCH 002/123] Remove duplication in pthread_mutex.cpp. Also add unit tests about thread woken up by pthread_mutex_unlock. Bug: 19216648 Change-Id: I8bde8105b00186c52a2f41d92458ae4a5eb90426 --- libc/bionic/pthread_mutex.cpp | 311 ++++++++++++---------------------- tests/pthread_test.cpp | 100 ++++++++--- 2 files changed, 190 insertions(+), 221 deletions(-) diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index 83d6b541a..f1107823e 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -237,7 +237,7 @@ int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared) return 0; } -static inline atomic_int* MUTEX_TO_ATOMIC_POINTER(pthread_mutex_t* mutex) { +static inline atomic_int* get_mutex_value_pointer(pthread_mutex_t* mutex) { static_assert(sizeof(atomic_int) == sizeof(mutex->value), "mutex->value should actually be atomic_int in implementation."); @@ -247,7 +247,7 @@ static inline atomic_int* MUTEX_TO_ATOMIC_POINTER(pthread_mutex_t* mutex) { } int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) { - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); if (__predict_true(attr == NULL)) { atomic_init(mutex_value_ptr, MUTEX_TYPE_BITS_NORMAL); @@ -277,6 +277,19 @@ int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) return 0; } +static inline int __pthread_normal_mutex_trylock(atomic_int* mutex_value_ptr, int shared) { + const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; + const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + + int mvalue = unlocked; + if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, + locked_uncontended, + memory_order_acquire, + memory_order_relaxed))) { + return 0; + } + return EBUSY; +} /* * Lock a mutex of type NORMAL. @@ -290,25 +303,17 @@ int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) * "type" value is zero, so the only bits that will be set are the ones in * the lock state field. */ -static inline void _normal_mutex_lock(atomic_int* mutex_value_ptr, int shared) { - /* convenience shortcuts */ - const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - - // The common case is an unlocked mutex, so we begin by trying to - // change the lock's state from unlocked to locked_uncontended. - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. - int mvalue = unlocked; - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, - locked_uncontended, - memory_order_acquire, - memory_order_relaxed))) { - return; +static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int shared, + const timespec* abs_timeout_or_null, clockid_t clock) { + if (__predict_true(__normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { + return 0; } ScopedTrace trace("Contending for pthread mutex"); + const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; + const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; + // We want to go to sleep until the mutex is available, which requires // promoting it to locked_contended. We need to swap in the new state // value and then wait until somebody wakes us up. @@ -316,20 +321,29 @@ static inline void _normal_mutex_lock(atomic_int* mutex_value_ptr, int shared) { // If it returns unlocked, we have acquired the lock, otherwise another // thread still holds the lock and we should wait again. // If lock is acquired, an acquire fence is needed to make all memory accesses - // made by other threads visible in current CPU. - const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; + // made by other threads visible to the current CPU. while (atomic_exchange_explicit(mutex_value_ptr, locked_contended, memory_order_acquire) != unlocked) { - - __futex_wait_ex(mutex_value_ptr, shared, locked_contended, NULL); + timespec ts; + timespec* rel_timeout = NULL; + if (abs_timeout_or_null != NULL) { + rel_timeout = &ts; + if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) { + return ETIMEDOUT; + } + } + if (__futex_wait_ex(mutex_value_ptr, shared, locked_contended, rel_timeout) == -ETIMEDOUT) { + return ETIMEDOUT; + } } + return 0; } /* * Release a mutex of type NORMAL. The caller is responsible for determining * that we are in fact the owner of this lock. */ -static inline void _normal_mutex_unlock(atomic_int* mutex_value_ptr, int shared) { +static inline void __pthread_normal_mutex_unlock(atomic_int* mutex_value_ptr, int shared) { const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; @@ -362,25 +376,13 @@ static inline void _normal_mutex_unlock(atomic_int* mutex_value_ptr, int shared) } } -/* This common inlined function is used to increment the counter of an - * errorcheck or recursive mutex. +/* This common inlined function is used to increment the counter of a recursive mutex. * - * For errorcheck mutexes, it will return EDEADLK - * If the counter overflows, it will return EAGAIN - * Otherwise, it atomically increments the counter and returns 0 - * after providing an acquire barrier. + * If the counter overflows, it will return EAGAIN. + * Otherwise, it atomically increments the counter and returns 0. * - * mtype is the current mutex type - * mvalue is the current mutex value (already loaded) - * mutex pointers to the mutex. */ -static inline __always_inline -int _recursive_increment(atomic_int* mutex_value_ptr, int mvalue, int mtype) { - if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { - // Trying to re-lock a mutex we already acquired. - return EDEADLK; - } - +static inline int __recursive_increment(atomic_int* mutex_value_ptr, int mvalue) { // Detect recursive lock overflow and return EAGAIN. // This is safe because only the owner thread can modify the // counter bits in the mutex value. @@ -393,15 +395,13 @@ int _recursive_increment(atomic_int* mutex_value_ptr, int mvalue, int mtype) { // loop to update the counter. The counter will not overflow in the loop, // as only the owner thread can change it. // The mutex is still locked, so we don't need a release fence. - while (!atomic_compare_exchange_weak_explicit(mutex_value_ptr, &mvalue, - mvalue + MUTEX_COUNTER_BITS_ONE, - memory_order_relaxed, - memory_order_relaxed)) { } + atomic_fetch_add_explicit(mutex_value_ptr, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return 0; } -int pthread_mutex_lock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); +static int __pthread_mutex_lock_with_timeout(pthread_mutex_t* mutex, + const timespec* abs_timeout_or_null, clockid_t clock) { + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); int mvalue, mtype, tid, shared; @@ -411,24 +411,28 @@ int pthread_mutex_lock(pthread_mutex_t* mutex) { // Handle common case first. if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) { - _normal_mutex_lock(mutex_value_ptr, shared); - return 0; + return __pthread_normal_mutex_lock(mutex_value_ptr, shared, abs_timeout_or_null, clock); } // Do we already own this recursive or error-check mutex? tid = __get_thread()->tid; - if ( tid == MUTEX_OWNER_FROM_BITS(mvalue) ) - return _recursive_increment(mutex_value_ptr, mvalue, mtype); + if (tid == MUTEX_OWNER_FROM_BITS(mvalue)) { + if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { + return EDEADLK; + } + return __recursive_increment(mutex_value_ptr, mvalue); + } - // Add in shared state to avoid extra 'or' operations below. - mtype |= shared; + const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + const int locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + const int locked_contended = mtype | shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; // First, if the mutex is unlocked, try to quickly acquire it. // In the optimistic case where this works, set the state to locked_uncontended. - if (mvalue == mtype) { - int newval = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. + if (mvalue == unlocked) { + int newval = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; + // If exchanged successfully, an acquire fence is required to make + // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, newval, memory_order_acquire, memory_order_relaxed))) { return 0; @@ -438,16 +442,14 @@ int pthread_mutex_lock(pthread_mutex_t* mutex) { ScopedTrace trace("Contending for pthread mutex"); while (true) { - if (mvalue == mtype) { - // If the mutex is unlocked, its value should be 'mtype' and - // we try to acquire it by setting its owner and state atomically. + if (mvalue == unlocked) { // NOTE: We put the state to locked_contended since we _know_ there // is contention when we are in this loop. This ensures all waiters // will be unlocked. - int newval = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_CONTENDED; - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. + int newval = MUTEX_OWNER_TO_BITS(tid) | locked_contended; + // If exchanged successfully, an acquire fence is required to make + // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_weak_explicit(mutex_value_ptr, &mvalue, newval, memory_order_acquire, @@ -456,8 +458,7 @@ int pthread_mutex_lock(pthread_mutex_t* mutex) { } continue; } else if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(mvalue)) { - // The mutex is already locked by another thread, if the state is locked_uncontended, - // we should set it to locked_contended beforing going to sleep. This can make + // We should set it to locked_contended beforing going to sleep. This can make // sure waiters will be woken up eventually. int newval = MUTEX_STATE_BITS_FLIP_CONTENTION(mvalue); @@ -470,14 +471,39 @@ int pthread_mutex_lock(pthread_mutex_t* mutex) { mvalue = newval; } - // We are in locked_contended state, sleep until someone wake us up. - __futex_wait_ex(mutex_value_ptr, shared, mvalue, NULL); + // We are in locked_contended state, sleep until someone wakes us up. + timespec ts; + timespec* rel_timeout = NULL; + if (abs_timeout_or_null != NULL) { + rel_timeout = &ts; + if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) { + return ETIMEDOUT; + } + } + if (__futex_wait_ex(mutex_value_ptr, shared, mvalue, rel_timeout) == -ETIMEDOUT) { + return ETIMEDOUT; + } mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); } } +int pthread_mutex_lock(pthread_mutex_t* mutex) { + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); + + int mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); + int mtype = (mvalue & MUTEX_TYPE_MASK); + int shared = (mvalue & MUTEX_SHARED_MASK); + // Avoid slowing down fast path of normal mutex lock operation. + if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { + if (__predict_true(__pthread_normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { + return 0; + } + } + return __pthread_mutex_lock_with_timeout(mutex, NULL, 0); +} + int pthread_mutex_unlock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); int mvalue, mtype, tid, shared; @@ -487,7 +513,7 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) { // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - _normal_mutex_unlock(mutex_value_ptr, shared); + __pthread_normal_mutex_unlock(mutex_value_ptr, shared); return 0; } @@ -501,10 +527,7 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) { // lower state bits), use a compare_exchange loop to do it. if (!MUTEX_COUNTER_BITS_IS_ZERO(mvalue)) { // We still own the mutex, so a release fence is not needed. - while (!atomic_compare_exchange_weak_explicit(mutex_value_ptr, &mvalue, - mvalue - MUTEX_COUNTER_BITS_ONE, - memory_order_relaxed, - memory_order_relaxed)) { } + atomic_fetch_sub_explicit(mutex_value_ptr, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return 0; } @@ -514,9 +537,8 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) { // to awake. // A release fence is required to make previous stores visible to next // lock owner threads. - mvalue = atomic_exchange_explicit(mutex_value_ptr, - mtype | shared | MUTEX_STATE_BITS_UNLOCKED, - memory_order_release); + const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + mvalue = atomic_exchange_explicit(mutex_value_ptr, unlocked, memory_order_release); if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(mvalue)) { __futex_wake_ex(mutex_value_ptr, shared, 1); } @@ -525,25 +547,18 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) { } int pthread_mutex_trylock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); int mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); int mtype = (mvalue & MUTEX_TYPE_MASK); int shared = (mvalue & MUTEX_SHARED_MASK); + const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + const int locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - mvalue = shared | MUTEX_STATE_BITS_UNLOCKED; - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. - if (atomic_compare_exchange_strong_explicit(mutex_value_ptr, - &mvalue, - shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED, - memory_order_acquire, - memory_order_relaxed)) { - return 0; - } - return EBUSY; + return __pthread_normal_mutex_trylock(mutex_value_ptr, shared); } // Do we already own this recursive or error-check mutex? @@ -552,19 +567,17 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EBUSY; } - return _recursive_increment(mutex_value_ptr, mvalue, mtype); + return __recursive_increment(mutex_value_ptr, mvalue); } // Same as pthread_mutex_lock, except that we don't want to wait, and // the only operation that can succeed is a single compare_exchange to acquire the // lock if it is released / not owned by anyone. No need for a complex loop. - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. - mtype |= shared | MUTEX_STATE_BITS_UNLOCKED; - mvalue = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, - &mtype, mvalue, + // If exchanged successfully, an acquire fence is required to make + // all memory accesses made by other threads visible to the current CPU. + mvalue = unlocked; + int newval = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; + if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, newval, memory_order_acquire, memory_order_relaxed))) { return 0; @@ -572,112 +585,6 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) { return EBUSY; } -static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_ts, clockid_t clock) { - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); - - timespec ts; - - int mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - int mtype = (mvalue & MUTEX_TYPE_MASK); - int shared = (mvalue & MUTEX_SHARED_MASK); - - // Handle common case first. - if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; - - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. - mvalue = unlocked; - if (atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, locked_uncontended, - memory_order_acquire, memory_order_relaxed)) { - return 0; - } - - ScopedTrace trace("Contending for timed pthread mutex"); - - // Same as pthread_mutex_lock, except that we can only wait for a specified - // time interval. If lock is acquired, an acquire fence is needed to make - // all memory accesses made by other threads visible in current CPU. - while (atomic_exchange_explicit(mutex_value_ptr, locked_contended, - memory_order_acquire) != unlocked) { - if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { - return ETIMEDOUT; - } - __futex_wait_ex(mutex_value_ptr, shared, locked_contended, &ts); - } - - return 0; - } - - // Do we already own this recursive or error-check mutex? - pid_t tid = __get_thread()->tid; - if (tid == MUTEX_OWNER_FROM_BITS(mvalue)) { - return _recursive_increment(mutex_value_ptr, mvalue, mtype); - } - - mtype |= shared; - - // First try a quick lock. - if (mvalue == mtype) { - int newval = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - // If exchanged successfully, An acquire fence is required to make - // all memory accesses made by other threads visible in current CPU. - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, - &mvalue, newval, - memory_order_acquire, - memory_order_relaxed))) { - return 0; - } - } - - ScopedTrace trace("Contending for timed pthread mutex"); - - // The following implements the same loop as pthread_mutex_lock, - // but adds checks to ensure that the operation never exceeds the - // absolute expiration time. - while (true) { - if (mvalue == mtype) { // Unlocked. - int newval = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_CONTENDED; - // An acquire fence is needed for successful exchange. - if (!atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, newval, - memory_order_acquire, - memory_order_relaxed)) { - goto check_time; - } - - return 0; - } else if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(mvalue)) { - // The value is locked. If the state is locked_uncontended, we need to switch - // it to locked_contended before sleep, so we can get woken up later. - int newval = MUTEX_STATE_BITS_FLIP_CONTENTION(mvalue); - if (!atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, newval, - memory_order_relaxed, - memory_order_relaxed)) { - goto check_time; - } - mvalue = newval; - } - - if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { - return ETIMEDOUT; - } - - if (__futex_wait_ex(mutex_value_ptr, shared, mvalue, &ts) == -ETIMEDOUT) { - return ETIMEDOUT; - } - -check_time: - if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) { - return ETIMEDOUT; - } - // After futex_wait or time costly timespec_from_absolte_timespec, - // we'd better read mvalue again in case it is changed. - mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - } -} - #if !defined(__LP64__) extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms) { timespec abs_timeout; @@ -689,7 +596,7 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms abs_timeout.tv_nsec -= NS_PER_S; } - int error = __pthread_mutex_timedlock(mutex, &abs_timeout, CLOCK_MONOTONIC); + int error = __pthread_mutex_lock_with_timeout(mutex, &abs_timeout, CLOCK_MONOTONIC); if (error == ETIMEDOUT) { error = EBUSY; } @@ -698,7 +605,7 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms #endif int pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout) { - return __pthread_mutex_timedlock(mutex, abs_timeout, CLOCK_REALTIME); + return __pthread_mutex_lock_with_timeout(mutex, abs_timeout, CLOCK_REALTIME); } int pthread_mutex_destroy(pthread_mutex_t* mutex) { @@ -708,7 +615,7 @@ int pthread_mutex_destroy(pthread_mutex_t* mutex) { return error; } - atomic_int* mutex_value_ptr = MUTEX_TO_ATOMIC_POINTER(mutex); + atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); atomic_store_explicit(mutex_value_ptr, 0xdead10cc, memory_order_relaxed); return 0; } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index c507faab9..b5c4a466c 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -733,8 +733,10 @@ TEST(pthread, pthread_rwlock_reader_wakeup_writer) { pthread_t thread; ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast(pthread_rwlock_reader_wakeup_writer_helper), &wakeup_arg)); - sleep(1); - ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress); + while (wakeup_arg.progress != RwlockWakeupHelperArg::LOCK_WAITING) { + usleep(5000); + } + usleep(5000); wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_RELEASED; ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock)); @@ -764,8 +766,10 @@ TEST(pthread, pthread_rwlock_writer_wakeup_reader) { pthread_t thread; ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast(pthread_rwlock_writer_wakeup_reader_helper), &wakeup_arg)); - sleep(1); - ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress); + while (wakeup_arg.progress != RwlockWakeupHelperArg::LOCK_WAITING) { + usleep(5000); + } + usleep(5000); wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_RELEASED; ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock)); @@ -1118,15 +1122,21 @@ TEST(pthread, pthread_mutexattr_gettype) { ASSERT_EQ(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); ASSERT_EQ(0, pthread_mutexattr_gettype(&attr, &attr_type)); ASSERT_EQ(PTHREAD_MUTEX_RECURSIVE, attr_type); + + ASSERT_EQ(0, pthread_mutexattr_destroy(&attr)); +} + +static void CreateMutex(pthread_mutex_t& mutex, int mutex_type) { + pthread_mutexattr_t attr; + ASSERT_EQ(0, pthread_mutexattr_init(&attr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&attr, mutex_type)); + ASSERT_EQ(0, pthread_mutex_init(&mutex, &attr)); + ASSERT_EQ(0, pthread_mutexattr_destroy(&attr)); } TEST(pthread, pthread_mutex_lock_NORMAL) { - pthread_mutexattr_t attr; - ASSERT_EQ(0, pthread_mutexattr_init(&attr)); - ASSERT_EQ(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL)); - pthread_mutex_t lock; - ASSERT_EQ(0, pthread_mutex_init(&lock, &attr)); + CreateMutex(lock, PTHREAD_MUTEX_NORMAL); ASSERT_EQ(0, pthread_mutex_lock(&lock)); ASSERT_EQ(0, pthread_mutex_unlock(&lock)); @@ -1134,12 +1144,8 @@ TEST(pthread, pthread_mutex_lock_NORMAL) { } TEST(pthread, pthread_mutex_lock_ERRORCHECK) { - pthread_mutexattr_t attr; - ASSERT_EQ(0, pthread_mutexattr_init(&attr)); - ASSERT_EQ(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); - pthread_mutex_t lock; - ASSERT_EQ(0, pthread_mutex_init(&lock, &attr)); + CreateMutex(lock, PTHREAD_MUTEX_ERRORCHECK); ASSERT_EQ(0, pthread_mutex_lock(&lock)); ASSERT_EQ(EDEADLK, pthread_mutex_lock(&lock)); @@ -1152,12 +1158,8 @@ TEST(pthread, pthread_mutex_lock_ERRORCHECK) { } TEST(pthread, pthread_mutex_lock_RECURSIVE) { - pthread_mutexattr_t attr; - ASSERT_EQ(0, pthread_mutexattr_init(&attr)); - ASSERT_EQ(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); - pthread_mutex_t lock; - ASSERT_EQ(0, pthread_mutex_init(&lock, &attr)); + CreateMutex(lock, PTHREAD_MUTEX_RECURSIVE); ASSERT_EQ(0, pthread_mutex_lock(&lock)); ASSERT_EQ(0, pthread_mutex_lock(&lock)); @@ -1169,6 +1171,66 @@ TEST(pthread, pthread_mutex_lock_RECURSIVE) { ASSERT_EQ(0, pthread_mutex_destroy(&lock)); } +class MutexWakeupHelper { + private: + pthread_mutex_t mutex; + enum Progress { + LOCK_INITIALIZED, + LOCK_WAITING, + LOCK_RELEASED, + LOCK_ACCESSED + }; + std::atomic progress; + + static void thread_fn(MutexWakeupHelper* helper) { + ASSERT_EQ(LOCK_INITIALIZED, helper->progress); + helper->progress = LOCK_WAITING; + + ASSERT_EQ(0, pthread_mutex_lock(&helper->mutex)); + ASSERT_EQ(LOCK_RELEASED, helper->progress); + ASSERT_EQ(0, pthread_mutex_unlock(&helper->mutex)); + + helper->progress = LOCK_ACCESSED; + } + + public: + void test(int mutex_type) { + CreateMutex(mutex, mutex_type); + ASSERT_EQ(0, pthread_mutex_lock(&mutex)); + progress = LOCK_INITIALIZED; + + pthread_t thread; + ASSERT_EQ(0, pthread_create(&thread, NULL, + reinterpret_cast(MutexWakeupHelper::thread_fn), this)); + + while (progress != LOCK_WAITING) { + usleep(5000); + } + usleep(5000); + progress = LOCK_RELEASED; + ASSERT_EQ(0, pthread_mutex_unlock(&mutex)); + + ASSERT_EQ(0, pthread_join(thread, NULL)); + ASSERT_EQ(LOCK_ACCESSED, progress); + ASSERT_EQ(0, pthread_mutex_destroy(&mutex)); + } +}; + +TEST(pthread, pthread_mutex_NORMAL_wakeup) { + MutexWakeupHelper helper; + helper.test(PTHREAD_MUTEX_NORMAL); +} + +TEST(pthread, pthread_mutex_ERRORCHECK_wakeup) { + MutexWakeupHelper helper; + helper.test(PTHREAD_MUTEX_ERRORCHECK); +} + +TEST(pthread, pthread_mutex_RECURSIVE_wakeup) { + MutexWakeupHelper helper; + helper.test(PTHREAD_MUTEX_RECURSIVE); +} + TEST(pthread, pthread_mutex_owner_tid_limit) { FILE* fp = fopen("/proc/sys/kernel/pid_max", "r"); ASSERT_TRUE(fp != NULL); From aef719510a57274e73ca02ab5ecdc5bf17d3985c Mon Sep 17 00:00:00 2001 From: Simon Baldwin Date: Fri, 16 Jan 2015 13:22:54 +0000 Subject: [PATCH 003/123] Support loading shared libraries from zip files Add code to support loading shared libraries directly from within APK files. Extends the linker's handling of LD_LIBRARY_PATH, DT_RUNPATH, etc to allow elements to be either directories as normal, or ZIP format files. For ZIP, the ZIP subdirectory string is separated from the path to file by '!'. For example, if DT_NEEDED is libchrome.so and Chrome.apk is the Android ARM APK then the path element /system/app/Chrome.apk!lib/armeabi-v7a would cause the linker to load lib/armeabi-v7a/libchrome.so directly from inside Chrome.apk. For loading to succeed, libchrome.so must be 'stored' and not compressed in Chrome.apk, and must be page aligned within the file. Motivation: Chromium tracking issue: https://code.google.com/p/chromium/issues/detail?id=390618 Bug: 8076853 Change-Id: Ic49046600b1417eae3ee8f37ee98c8ac1ecc19e7 --- linker/Android.mk | 2 +- linker/linker.cpp | 119 ++++++++++++++++++---- tests/dlfcn_test.cpp | 48 +++++++++ tests/libs/Android.build.dlext_testzip.mk | 6 +- 4 files changed, 153 insertions(+), 22 deletions(-) diff --git a/linker/Android.mk b/linker/Android.mk index f78a0257c..81c007f3e 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -57,7 +57,7 @@ LOCAL_ASFLAGS := $(LOCAL_CFLAGS) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -LOCAL_STATIC_LIBRARIES := libc_nomalloc +LOCAL_STATIC_LIBRARIES := libc_nomalloc libziparchive libutils libz liblog LOCAL_FORCE_STATIC_EXECUTABLE := true diff --git a/linker/linker.cpp b/linker/linker.cpp index 18f8cdcb4..15ea423f9 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -57,6 +57,7 @@ #include "linker_phdr.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" +#include "ziparchive/zip_archive.h" /* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< * @@ -838,29 +839,109 @@ ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) { return nullptr; } -static int open_library_on_path(const char* name, const char* const paths[]) { - char buf[512]; - for (size_t i = 0; paths[i] != nullptr; ++i) { - int n = __libc_format_buffer(buf, sizeof(buf), "%s/%s", paths[i], name); - if (n < 0 || n >= static_cast(sizeof(buf))) { - PRINT("Warning: ignoring very long library path: %s/%s", paths[i], name); - continue; - } - int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); - if (fd != -1) { - return fd; - } +static int open_library_in_zipfile(const char* const path, + off64_t* file_offset) { + TRACE("Trying zip file open from path '%s'", path); + + // Treat an '!' character inside a path as the separator between the name + // of the zip file on disk and the subdirectory to search within it. + // For example, if path is "foo.zip!bar/bas/x.so", then we search for + // "bar/bas/x.so" within "foo.zip". + const char* separator = strchr(path, '!'); + if (separator == nullptr) { + return -1; } - return -1; + + char buf[512]; + if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) { + PRINT("Warning: ignoring very long library path: %s", path); + return -1; + } + + buf[separator - path] = '\0'; + + const char* zip_path = buf; + const char* file_path = &buf[separator - path + 1]; + int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + return -1; + } + + ZipArchiveHandle handle; + if (OpenArchiveFd(fd, "", &handle, false) != 0) { + // invalid zip-file (?) + close(fd); + return -1; + } + + auto archive_guard = make_scope_guard([&]() { + CloseArchive(handle); + }); + + ZipEntry entry; + + if (FindEntry(handle, ZipEntryName(file_path), &entry) != 0) { + // Entry was not found. + close(fd); + return -1; + } + + // Check if it is properly stored + if (entry.method != kCompressStored || (entry.offset % PAGE_SIZE) != 0) { + close(fd); + return -1; + } + + *file_offset = entry.offset; + return fd; } -static int open_library(const char* name) { +static int open_library_on_path(const char* name, + const char* const paths[], + off64_t* file_offset) { + char buf[512]; + int fd = -1; + + for (size_t i = 0; paths[i] != nullptr && fd == -1; ++i) { + const char* const path = paths[i]; + int n = __libc_format_buffer(buf, sizeof(buf), "%s/%s", path, name); + if (n < 0 || n >= static_cast(sizeof(buf))) { + PRINT("Warning: ignoring very long library path: %s/%s", path, name); + return -1; + } + + const char* separator = strchr(path, '!'); + + if (separator != nullptr) { + fd = open_library_in_zipfile(buf, file_offset); + } + + if (fd == -1) { + fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + *file_offset = 0; + } + } + } + + return fd; +} + +static int open_library(const char* name, off64_t* file_offset) { TRACE("[ opening %s ]", name); // If the name contains a slash, we should attempt to open it directly and not search the paths. if (strchr(name, '/') != nullptr) { + if (strchr(name, '!') != nullptr) { + int fd = open_library_in_zipfile(name, file_offset); + if (fd != -1) { + return fd; + } + } + int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); if (fd != -1) { + *file_offset = 0; return fd; } // ...but nvidia binary blobs (at least) rely on this behavior, so fall through for now. @@ -870,9 +951,9 @@ static int open_library(const char* name) { } // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths. - int fd = open_library_on_path(name, g_ld_library_paths); + int fd = open_library_on_path(name, g_ld_library_paths, file_offset); if (fd == -1) { - fd = open_library_on_path(name, kDefaultLdPaths); + fd = open_library_on_path(name, kDefaultLdPaths, file_offset); } return fd; } @@ -886,7 +967,9 @@ static void for_each_dt_needed(const soinfo* si, F action) { } } -static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) { +static soinfo* load_library(LoadTaskList& load_tasks, + const char* name, int rtld_flags, + const android_dlextinfo* extinfo) { int fd = -1; off64_t file_offset = 0; ScopedFd file_guard(-1); @@ -898,7 +981,7 @@ static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld } } else { // Open the file. - fd = open_library(name); + fd = open_library(name, &file_offset); if (fd == -1) { DL_ERR("library \"%s\" not found", name); return nullptr; diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index dfb5e54ab..ef4ff20f0 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -29,6 +29,14 @@ #define ASSERT_SUBSTR(needle, haystack) \ ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack) +#if defined(__LP64__) +#define LIBPATH_PREFIX "/nativetest64/libdlext_test_fd/" +#else +#define LIBPATH_PREFIX "/nativetest/libdlext_test_fd/" +#endif + +#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_fd_zipaligned.zip" + static bool g_called = false; extern "C" void DlSymTestFunction() { g_called = true; @@ -844,6 +852,46 @@ TEST(dlfcn, dlopen_symlink) { dlclose(handle2); } +TEST(dlfcn, dlopen_from_zip_absolute_path) { + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; + + void* handle = dlopen((lib_path + "!libdir/libdlext_test_fd.so").c_str(), RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + int (*fn)(void); + fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); + ASSERT_TRUE(fn != nullptr); + EXPECT_EQ(4, fn()); + + dlclose(handle); +} + +TEST(dlfcn, dlopen_from_zip_ld_library_path) { + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!libdir"; + + typedef void (*fn_t)(const char*); + fn_t android_update_LD_LIBRARY_PATH = + reinterpret_cast(dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH")); + + ASSERT_TRUE(android_update_LD_LIBRARY_PATH != nullptr) << dlerror(); + + void* handle = dlopen("libdlext_test_fd.so", RTLD_NOW); + ASSERT_TRUE(handle == nullptr); + + android_update_LD_LIBRARY_PATH(lib_path.c_str()); + + handle = dlopen("libdlext_test_fd.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + int (*fn)(void); + fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); + ASSERT_TRUE(fn != nullptr); + EXPECT_EQ(4, fn()); + + dlclose(handle); +} + + // libtest_dlopen_from_ctor_main.so depends on // libtest_dlopen_from_ctor.so which has a constructor // that calls dlopen(libc...). This is to test the situation diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk index d05927ec2..7cc0dae90 100644 --- a/tests/libs/Android.build.dlext_testzip.mk +++ b/tests/libs/Android.build.dlext_testzip.mk @@ -35,7 +35,7 @@ my_shared_libs := \ $(LOCAL_BUILT_MODULE): PRIVATE_ALIGNMENT := 4096 # PAGE_SIZE $(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(ZIPALIGN) @echo "Zipalign $(PRIVATE_ALIGNMENT): $@" - $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) - $(hide) cp $^ $(dir $@) - $(hide) (cd $(dir $@) && touch empty_file.txt && zip -rD0 $(notdir $@).unaligned empty_file.txt *.so) + $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir + $(hide) cp $^ $(dir $@)/libdir + $(hide) (cd $(dir $@) && touch empty_file.txt && zip -rD0 $(notdir $@).unaligned empty_file.txt libdir/*.so) $(hide) $(ZIPALIGN) $(PRIVATE_ALIGNMENT) $@.unaligned $@ From ecbfb25c504b7360d250c849ab47890ad54b6125 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 17 Mar 2015 11:34:57 -0700 Subject: [PATCH 004/123] Fix build: pthread_mutex/pthread_detach. Change-Id: I9c7b6297d3bf3ab8004d05d44cc4c95159315c9e --- libc/bionic/pthread_detach.cpp | 4 ++-- libc/bionic/pthread_mutex.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index 0712d0d67..7ae5eb4c6 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -44,9 +44,9 @@ int pthread_detach(pthread_t t) { } switch (old_state) { case THREAD_NOT_JOINED: return 0; - case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc. + case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc. case THREAD_DETACHED: return THREAD_DETACHED; - case THREAD_EXITED_NOT_JOINED: // Call pthread_join out of scope of pthread_accessor. + case THREAD_EXITED_NOT_JOINED: break; // Call pthread_join out of scope of pthread_accessor. } } diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index f1107823e..24066ae02 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -305,7 +305,7 @@ static inline int __pthread_normal_mutex_trylock(atomic_int* mutex_value_ptr, in */ static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int shared, const timespec* abs_timeout_or_null, clockid_t clock) { - if (__predict_true(__normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { + if (__predict_true(__pthread_normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { return 0; } From dc93db20a96e95827f4873fa04bb1e58177736df Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 17 Mar 2015 11:19:07 -0700 Subject: [PATCH 005/123] Change name of rwlock benchmark. rw_lock is not easy for me to remember, so I prefer changing it to rwlock. Change-Id: I0784acaca0c3b46c28184a77eb29ffe696f9ea07 --- benchmarks/pthread_benchmark.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/pthread_benchmark.cpp b/benchmarks/pthread_benchmark.cpp index 2f6572dd8..ad31e7e8c 100644 --- a/benchmarks/pthread_benchmark.cpp +++ b/benchmarks/pthread_benchmark.cpp @@ -121,8 +121,8 @@ void BM_pthread_mutex_lock_RECURSIVE::Run(int iters) { StopBenchmarkTiming(); } -BENCHMARK_NO_ARG(BM_pthread_rw_lock_read); -void BM_pthread_rw_lock_read::Run(int iters) { +BENCHMARK_NO_ARG(BM_pthread_rwlock_read); +void BM_pthread_rwlock_read::Run(int iters) { StopBenchmarkTiming(); pthread_rwlock_t lock; pthread_rwlock_init(&lock, NULL); @@ -137,8 +137,8 @@ void BM_pthread_rw_lock_read::Run(int iters) { pthread_rwlock_destroy(&lock); } -BENCHMARK_NO_ARG(BM_pthread_rw_lock_write); -void BM_pthread_rw_lock_write::Run(int iters) { +BENCHMARK_NO_ARG(BM_pthread_rwlock_write); +void BM_pthread_rwlock_write::Run(int iters) { StopBenchmarkTiming(); pthread_rwlock_t lock; pthread_rwlock_init(&lock, NULL); From ee7649c5ac5f1e56cc8193cd4cee73004c04893d Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sun, 15 Mar 2015 21:39:25 -0400 Subject: [PATCH 006/123] set errno to ENOENT in getauxval per glibc 2.19 Bionic's getauxval(...) implementation returns zero when entries are missing. Zero can be a valid value, so there is no unambiguous way of detecting an error. Since glibc 2.19, errno is set to ENOENT when an entry is missing to make it possible to detect this. Bionic should match this behavior as code in the Linux ecosystem will start relying on it to check for the presence of newly added entries. Change-Id: Ic1efe29bc45fc87489274c96c4d2193f3a7b8854 Signed-off-by: Daniel Micay --- libc/bionic/getauxval.cpp | 2 ++ tests/getauxval_test.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp index bc4182496..22922b942 100644 --- a/libc/bionic/getauxval.cpp +++ b/libc/bionic/getauxval.cpp @@ -31,6 +31,7 @@ #include #include #include +#include __LIBC_HIDDEN__ ElfW(auxv_t)* __libc_auxv = NULL; @@ -40,5 +41,6 @@ extern "C" unsigned long int getauxval(unsigned long int type) { return v->a_un.a_val; } } + errno = ENOENT; return 0; } diff --git a/tests/getauxval_test.cpp b/tests/getauxval_test.cpp index b33115007..6ce00f14a 100644 --- a/tests/getauxval_test.cpp +++ b/tests/getauxval_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -53,7 +54,9 @@ TEST(getauxval, expected_values) { TEST(getauxval, unexpected_values) { #if defined(GETAUXVAL_CAN_COMPILE) + errno = 0; ASSERT_EQ((unsigned long int) 0, getauxval(0xdeadbeef)); + ASSERT_EQ(ENOENT, errno); #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif From 110196244732911d4971ef7f9e30cc2407d8034b Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 17 Mar 2015 17:34:14 -0700 Subject: [PATCH 007/123] Fix _PATH_DEFPATH to correspond to the actual default path. This is currently set in init.rc, but I plan on making init set PATH to _PATH_DEFPATH and removing the line from init.rc... Bug: 19564110 Change-Id: Ifa7226a3a5a90d141a788d7d6b1ae86245674218 --- libc/include/paths.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libc/include/paths.h b/libc/include/paths.h index 33c2eee1b..82c28042f 100644 --- a/libc/include/paths.h +++ b/libc/include/paths.h @@ -32,18 +32,13 @@ #ifndef _PATHS_H_ #define _PATHS_H_ -/* Default search path. */ -#define _PATH_DEFPATH "/system/bin:/system/xbin" - #define _PATH_BSHELL "/system/bin/sh" #define _PATH_CONSOLE "/dev/console" +#define _PATH_DEFPATH "/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin" +#define _PATH_DEV "/dev/" #define _PATH_DEVNULL "/dev/null" #define _PATH_KLOG "/proc/kmsg" - #define _PATH_MOUNTED "/proc/mounts" #define _PATH_TTY "/dev/tty" -/* Provide trailing slash, since mostly used for building pathnames. */ -#define _PATH_DEV "/dev/" - #endif /* !_PATHS_H_ */ From e44fffd7f9b93b9ec9836cfc7acedf7e21107f8f Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 17 Mar 2015 17:12:18 -0700 Subject: [PATCH 008/123] Fix library lookup for filenames with slash. If filename contains a slash, then it is interpreted as a (relative or absolute) pathname. Bug: https://code.google.com/p/android/issues/detail?id=6670 Change-Id: Iba57d638301f3089ad47ba083edca2dd36b801ed --- linker/linker.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 15ea423f9..3960f9aa8 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -942,12 +942,8 @@ static int open_library(const char* name, off64_t* file_offset) { int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); if (fd != -1) { *file_offset = 0; - return fd; } - // ...but nvidia binary blobs (at least) rely on this behavior, so fall through for now. -#if defined(__LP64__) - return -1; -#endif + return fd; } // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths. From fa495d51b02e1575088ed358614d3baa442f455f Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 18 Mar 2015 15:46:48 -0700 Subject: [PATCH 009/123] Hide statfs/fstatfs' ST_VALID flag from userspace. Spotted while debugging the strace 4.10 upgrade. Change-Id: I1af1be9c9440151f55f74a835e1df71529b0e4fe --- libc/SYSCALLS.TXT | 4 +-- .../syscalls/{fstatfs64.S => __fstatfs.S} | 8 ++--- .../syscalls/{statfs64.S => __statfs.S} | 8 ++--- .../syscalls/{fstatfs64.S => __fstatfs.S} | 8 ++--- .../syscalls/{statfs64.S => __statfs.S} | 8 ++--- .../syscalls/{fstatfs64.S => __fstatfs.S} | 8 ++--- .../syscalls/{statfs64.S => __statfs.S} | 8 ++--- libc/bionic/legacy_32_bit_support.cpp | 14 -------- libc/bionic/statvfs.cpp | 34 ++++++++++++++++--- tests/sys_statvfs_test.cpp | 6 ++++ tests/sys_vfs_test.cpp | 6 ++++ 11 files changed, 61 insertions(+), 51 deletions(-) rename libc/arch-arm64/syscalls/{fstatfs64.S => __fstatfs.S} (73%) rename libc/arch-arm64/syscalls/{statfs64.S => __statfs.S} (74%) rename libc/arch-mips64/syscalls/{fstatfs64.S => __fstatfs.S} (81%) rename libc/arch-mips64/syscalls/{statfs64.S => __statfs.S} (82%) rename libc/arch-x86_64/syscalls/{fstatfs64.S => __fstatfs.S} (76%) rename libc/arch-x86_64/syscalls/{statfs64.S => __statfs.S} (77%) diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index 150dd14f9..b91f5bf57 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -174,9 +174,9 @@ int __fadvise64:fadvise64_64(int, off64_t, off64_t, int) x86 int __fadvise64:fadvise64(int, off64_t, off64_t, int) arm64,mips,mips64,x86_64 int __fstatfs64:fstatfs64(int, size_t, struct statfs*) arm,mips,x86 -int fstatfs64|fstatfs:fstatfs(int, struct statfs*) arm64,mips64,x86_64 +int __fstatfs:fstatfs(int, struct statfs*) arm64,mips64,x86_64 int __statfs64:statfs64(const char*, size_t, struct statfs*) arm,mips,x86 -int statfs64|statfs:statfs(const char*, struct statfs*) arm64,mips64,x86_64 +int __statfs:statfs(const char*, struct statfs*) arm64,mips64,x86_64 int fstat64|fstat:fstat64(int, struct stat*) arm,mips,x86 int fstat64|fstat:fstat(int, struct stat*) arm64,x86_64 diff --git a/libc/arch-arm64/syscalls/fstatfs64.S b/libc/arch-arm64/syscalls/__fstatfs.S similarity index 73% rename from libc/arch-arm64/syscalls/fstatfs64.S rename to libc/arch-arm64/syscalls/__fstatfs.S index 2ca2dcd7f..7e350d6c5 100644 --- a/libc/arch-arm64/syscalls/fstatfs64.S +++ b/libc/arch-arm64/syscalls/__fstatfs.S @@ -2,7 +2,7 @@ #include -ENTRY(fstatfs64) +ENTRY(__fstatfs) mov x8, __NR_fstatfs svc #0 @@ -11,7 +11,5 @@ ENTRY(fstatfs64) b.hi __set_errno_internal ret -END(fstatfs64) - - .globl fstatfs - .equ fstatfs, fstatfs64 +END(__fstatfs) +.hidden __fstatfs diff --git a/libc/arch-arm64/syscalls/statfs64.S b/libc/arch-arm64/syscalls/__statfs.S similarity index 74% rename from libc/arch-arm64/syscalls/statfs64.S rename to libc/arch-arm64/syscalls/__statfs.S index ec8c588df..962c59098 100644 --- a/libc/arch-arm64/syscalls/statfs64.S +++ b/libc/arch-arm64/syscalls/__statfs.S @@ -2,7 +2,7 @@ #include -ENTRY(statfs64) +ENTRY(__statfs) mov x8, __NR_statfs svc #0 @@ -11,7 +11,5 @@ ENTRY(statfs64) b.hi __set_errno_internal ret -END(statfs64) - - .globl statfs - .equ statfs, statfs64 +END(__statfs) +.hidden __statfs diff --git a/libc/arch-mips64/syscalls/fstatfs64.S b/libc/arch-mips64/syscalls/__fstatfs.S similarity index 81% rename from libc/arch-mips64/syscalls/fstatfs64.S rename to libc/arch-mips64/syscalls/__fstatfs.S index 12e885cf1..8766e22b5 100644 --- a/libc/arch-mips64/syscalls/fstatfs64.S +++ b/libc/arch-mips64/syscalls/__fstatfs.S @@ -2,7 +2,7 @@ #include -ENTRY(fstatfs64) +ENTRY(__fstatfs) .set push .set noreorder li v0, __NR_fstatfs @@ -22,7 +22,5 @@ ENTRY(fstatfs64) j t9 move ra, t0 .set pop -END(fstatfs64) - - .globl fstatfs - .equ fstatfs, fstatfs64 +END(__fstatfs) +.hidden __fstatfs diff --git a/libc/arch-mips64/syscalls/statfs64.S b/libc/arch-mips64/syscalls/__statfs.S similarity index 82% rename from libc/arch-mips64/syscalls/statfs64.S rename to libc/arch-mips64/syscalls/__statfs.S index 74351f7f5..52db4e2a8 100644 --- a/libc/arch-mips64/syscalls/statfs64.S +++ b/libc/arch-mips64/syscalls/__statfs.S @@ -2,7 +2,7 @@ #include -ENTRY(statfs64) +ENTRY(__statfs) .set push .set noreorder li v0, __NR_statfs @@ -22,7 +22,5 @@ ENTRY(statfs64) j t9 move ra, t0 .set pop -END(statfs64) - - .globl statfs - .equ statfs, statfs64 +END(__statfs) +.hidden __statfs diff --git a/libc/arch-x86_64/syscalls/fstatfs64.S b/libc/arch-x86_64/syscalls/__fstatfs.S similarity index 76% rename from libc/arch-x86_64/syscalls/fstatfs64.S rename to libc/arch-x86_64/syscalls/__fstatfs.S index f727350e1..b50e3553f 100644 --- a/libc/arch-x86_64/syscalls/fstatfs64.S +++ b/libc/arch-x86_64/syscalls/__fstatfs.S @@ -2,7 +2,7 @@ #include -ENTRY(fstatfs64) +ENTRY(__fstatfs) movl $__NR_fstatfs, %eax syscall cmpq $-MAX_ERRNO, %rax @@ -12,7 +12,5 @@ ENTRY(fstatfs64) call __set_errno_internal 1: ret -END(fstatfs64) - - .globl fstatfs - .equ fstatfs, fstatfs64 +END(__fstatfs) +.hidden __fstatfs diff --git a/libc/arch-x86_64/syscalls/statfs64.S b/libc/arch-x86_64/syscalls/__statfs.S similarity index 77% rename from libc/arch-x86_64/syscalls/statfs64.S rename to libc/arch-x86_64/syscalls/__statfs.S index 16f6bdd6b..607a809db 100644 --- a/libc/arch-x86_64/syscalls/statfs64.S +++ b/libc/arch-x86_64/syscalls/__statfs.S @@ -2,7 +2,7 @@ #include -ENTRY(statfs64) +ENTRY(__statfs) movl $__NR_statfs, %eax syscall cmpq $-MAX_ERRNO, %rax @@ -12,7 +12,5 @@ ENTRY(statfs64) call __set_errno_internal 1: ret -END(statfs64) - - .globl statfs - .equ statfs, statfs64 +END(__statfs) +.hidden __statfs diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp index 73f77bebf..50e4643ce 100644 --- a/libc/bionic/legacy_32_bit_support.cpp +++ b/libc/bionic/legacy_32_bit_support.cpp @@ -40,9 +40,7 @@ // System calls we need. extern "C" int __fcntl64(int, int, void*); -extern "C" int __fstatfs64(int, size_t, struct statfs*); extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int); -extern "C" int __statfs64(const char*, size_t, struct statfs*); // For fcntl we use the fcntl64 system call to signal that we're using struct flock64. int fcntl(int fd, int cmd, ...) { @@ -55,18 +53,6 @@ int fcntl(int fd, int cmd, ...) { return __fcntl64(fd, cmd, arg); } -// For fstatfs we need to add the extra argument giving the kernel the size of the buffer. -int fstatfs(int fd, struct statfs* stat) { - return __fstatfs64(fd, sizeof(*stat), stat); -} -__strong_alias(fstatfs64, fstatfs); - -// For statfs we need to add the extra argument giving the kernel the size of the buffer. -int statfs(const char* path, struct statfs* stat) { - return __statfs64(path, sizeof(*stat), stat); -} -__strong_alias(statfs64, statfs); - // For lseek64 we need to use the llseek system call which splits the off64_t in two and // returns the off64_t result via a pointer because 32-bit kernels can't return 64-bit results. off64_t lseek64(int fd, off64_t off, int whence) { diff --git a/libc/bionic/statvfs.cpp b/libc/bionic/statvfs.cpp index f1e283316..39ffb640f 100644 --- a/libc/bionic/statvfs.cpp +++ b/libc/bionic/statvfs.cpp @@ -21,13 +21,17 @@ // Paper over the fact that 32-bit kernels use fstatfs64/statfs64 with an extra argument, // but 64-bit kernels don't have the "64" bit suffix or the extra size_t argument. #if __LP64__ -# define __fstatfs64(fd,size,buf) fstatfs(fd,buf) -# define __statfs64(path,size,buf) statfs(path,buf) +extern "C" int __fstatfs(int, struct statfs*); +extern "C" int __statfs(const char*, struct statfs*); +# define __fstatfs64(fd,size,buf) __fstatfs(fd,buf) +# define __statfs64(path,size,buf) __statfs(path,buf) #else extern "C" int __fstatfs64(int, size_t, struct statfs*); extern "C" int __statfs64(const char*, size_t, struct statfs*); #endif +// The kernel sets a private ST_VALID flag to signal to the C library whether the +// f_flags field is valid. This flag should not be exposed to users of the C library. #define ST_VALID 0x0020 static void __statfs_to_statvfs(const struct statfs& in, struct statvfs* out) { @@ -40,13 +44,33 @@ static void __statfs_to_statvfs(const struct statfs& in, struct statvfs* out) { out->f_ffree = in.f_ffree; out->f_favail = in.f_ffree; out->f_fsid = in.f_fsid.__val[0] | (static_cast(in.f_fsid.__val[1]) << 32); - out->f_flag = in.f_flags & ~ST_VALID; + out->f_flag = in.f_flags; out->f_namemax = in.f_namelen; } +int fstatfs(int fd, struct statfs* result) { + int rc = __fstatfs64(fd, sizeof(*result), result); + if (rc != 0) { + return rc; + } + result->f_flags &= ~ST_VALID; + return 0; +} +__strong_alias(fstatfs64, fstatfs); + +int statfs(const char* path, struct statfs* result) { + int rc = __statfs64(path, sizeof(*result), result); + if (rc != 0) { + return rc; + } + result->f_flags &= ~ST_VALID; + return 0; +} +__strong_alias(statfs64, statfs); + int statvfs(const char* path, struct statvfs* result) { struct statfs tmp; - int rc = __statfs64(path, sizeof(tmp), &tmp); + int rc = statfs(path, &tmp); if (rc != 0) { return rc; } @@ -57,7 +81,7 @@ __strong_alias(statvfs64, statvfs); int fstatvfs(int fd, struct statvfs* result) { struct statfs tmp; - int rc = __fstatfs64(fd, sizeof(tmp), &tmp); + int rc = fstatfs(fd, &tmp); if (rc != 0) { return rc; } diff --git a/tests/sys_statvfs_test.cpp b/tests/sys_statvfs_test.cpp index 6b19e13b8..bff9e2035 100644 --- a/tests/sys_statvfs_test.cpp +++ b/tests/sys_statvfs_test.cpp @@ -30,6 +30,11 @@ template void Check(StatVfsT& sb) { EXPECT_EQ(0U, sb.f_ffree); EXPECT_EQ(0U, sb.f_fsid); EXPECT_EQ(255U, sb.f_namemax); + + // The kernel sets a private bit to indicate that f_flags is valid. + // This flag is not supposed to be exposed to libc clients. + static const uint32_t ST_VALID = 0x0020; + EXPECT_TRUE((sb.f_flag & ST_VALID) == 0) << sb.f_flag; } TEST(sys_statvfs, statvfs) { @@ -51,6 +56,7 @@ TEST(sys_statvfs, fstatvfs) { close(fd); Check(sb); } + TEST(sys_statvfs, fstatvfs64) { struct statvfs64 sb; int fd = open("/proc", O_RDONLY); diff --git a/tests/sys_vfs_test.cpp b/tests/sys_vfs_test.cpp index 4b056607b..a521967bf 100644 --- a/tests/sys_vfs_test.cpp +++ b/tests/sys_vfs_test.cpp @@ -31,6 +31,11 @@ template void Check(StatFsT& sb) { EXPECT_EQ(0, sb.f_fsid.__val[0]); EXPECT_EQ(0, sb.f_fsid.__val[1]); EXPECT_EQ(255, static_cast(sb.f_namelen)); + + // The kernel sets a private bit to indicate that f_flags is valid. + // This flag is not supposed to be exposed to libc clients. + static const uint32_t ST_VALID = 0x0020; + EXPECT_TRUE((sb.f_flags & ST_VALID) == 0) << sb.f_flags; } TEST(sys_vfs, statfs) { @@ -52,6 +57,7 @@ TEST(sys_vfs, fstatfs) { close(fd); Check(sb); } + TEST(sys_vfs, fstatfs64) { struct statfs64 sb; int fd = open("/proc", O_RDONLY); From 618f1a36f8635fa0f2d60c621fbf79ead2c3f3de Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 17 Mar 2015 20:06:36 -0700 Subject: [PATCH 010/123] Implement lookup by DT_SONAME This CL also fixes SEARCH_NAME hack and resolves https://code.google.com/p/android/issues/detail?id=6670 once and for all. Bug: https://code.google.com/p/android/issues/detail?id=6670 Change-Id: I9b8d6a672cd722f30fbfbb40cdee8d9b39cfe56e --- linker/dlfcn.cpp | 1 + linker/linker.cpp | 55 ++++++++++++++++++++++++++----------------- linker/linker.h | 6 ++++- tests/dlfcn_test.cpp | 26 ++++++++++++++++++++ tests/libs/Android.mk | 11 +++++++++ 5 files changed, 76 insertions(+), 23 deletions(-) diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 9a8dbc93f..64df7a570 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -248,6 +248,7 @@ soinfo* get_libdl_info() { __libdl_info.ref_count_ = 1; __libdl_info.strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB); __libdl_info.local_group_root_ = &__libdl_info; + __libdl_info.soname_ = "libdl.so"; } return &__libdl_info; diff --git a/linker/linker.cpp b/linker/linker.cpp index 3960f9aa8..44ae4d49d 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -78,19 +78,6 @@ #undef ELF_ST_TYPE #define ELF_ST_TYPE(x) (static_cast(x) & 0xf) -#if defined(__LP64__) -#define SEARCH_NAME(x) x -#else -// Nvidia drivers are relying on the bug: -// http://code.google.com/p/android/issues/detail?id=6670 -// so we continue to use base-name lookup for lp32 -static const char* get_base_name(const char* name) { - const char* bname = strrchr(name, '/'); - return bname ? bname + 1 : name; -} -#define SEARCH_NAME(x) get_base_name(x) -#endif - static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); static LinkerTypeAllocator g_soinfo_allocator; @@ -1029,7 +1016,7 @@ static soinfo* load_library(LoadTaskList& load_tasks, return nullptr; } - soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat, file_offset, rtld_flags); + soinfo* si = soinfo_alloc(name, &file_stat, file_offset, rtld_flags); if (si == nullptr) { return nullptr; } @@ -1051,10 +1038,15 @@ static soinfo* load_library(LoadTaskList& load_tasks, return si; } -static soinfo *find_loaded_library_by_name(const char* name) { - const char* search_name = SEARCH_NAME(name); +static soinfo *find_loaded_library_by_soname(const char* name) { + // Ignore filename with path. + if (strchr(name, '/') != nullptr) { + return nullptr; + } + for (soinfo* si = solist; si != nullptr; si = si->next) { - if (!strcmp(search_name, si->name)) { + const char* soname = si->get_soname(); + if (soname != nullptr && (strcmp(name, soname) == 0)) { return si; } } @@ -1062,13 +1054,12 @@ static soinfo *find_loaded_library_by_name(const char* name) { } static soinfo* find_library_internal(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) { - - soinfo* si = find_loaded_library_by_name(name); + soinfo* si = find_loaded_library_by_soname(name); // Library might still be loaded, the accurate detection // of this fact is done by load_library. if (si == nullptr) { - TRACE("[ '%s' has not been found by name. Trying harder...]", name); + TRACE("[ '%s' has not been found by soname. Trying harder...]", name); si = load_library(load_tasks, name, rtld_flags, extinfo); } @@ -1832,6 +1823,7 @@ uint32_t soinfo::get_dt_flags_1() const { return 0; } + void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { if (has_min_version(1)) { if ((dt_flags_1 & DF_1_GLOBAL) != 0) { @@ -1846,6 +1838,14 @@ void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { } } +const char* soinfo::get_soname() { + if (has_min_version(2)) { + return soname_; + } else { + return name; + } +} + // This is a return on get_children()/get_parents() if // 'this->flags' does not have FLAG_NEW_SOINFO set. static soinfo::soinfo_list_t g_empty_list; @@ -2012,14 +2012,17 @@ bool soinfo::prelink_image() { #endif // Extract useful information from dynamic section. + // Note that: "Except for the DT_NULL element at the end of the array, + // and the relative order of DT_NEEDED elements, entries may appear in any order." + // + // source: http://www.sco.com/developers/gabi/1998-04-29/ch5.dynamic.html uint32_t needed_count = 0; for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p", d, reinterpret_cast(d->d_tag), reinterpret_cast(d->d_un.d_val)); switch (d->d_tag) { case DT_SONAME: - // TODO: glibc dynamic linker uses this name for - // initial library lookup; consider doing the same here. + // this is parsed after we have strtab initialized (see below). break; case DT_HASH: @@ -2342,6 +2345,14 @@ bool soinfo::prelink_image() { } } + // second pass - parse entries relying on strtab + for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_SONAME) { + soname_ = get_string(d->d_un.d_val); + break; + } + } + DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p", reinterpret_cast(base), strtab_, symtab_); diff --git a/linker/linker.h b/linker/linker.h index e4681ebe5..04acda413 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -92,7 +92,7 @@ #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) -#define SOINFO_VERSION 1 +#define SOINFO_VERSION 2 #define SOINFO_NAME_LEN 128 @@ -278,6 +278,8 @@ struct soinfo { soinfo* get_local_group_root() const; + const char* get_soname(); + private: ElfW(Sym)* elf_lookup(SymbolName& symbol_name); ElfW(Sym)* elf_addr_lookup(const void* addr); @@ -322,6 +324,8 @@ struct soinfo { uint8_t* android_relocs_; size_t android_relocs_size_; + const char* soname_; + friend soinfo* get_libdl_info(); }; diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index ef4ff20f0..2bd9476fa 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -97,6 +97,32 @@ TEST(dlfcn, dlopen_noload) { ASSERT_EQ(0, dlclose(handle2)); } +TEST(dlfcn, dlopen_by_soname) { + static const char* soname = "libdlext_test_soname.so"; + static const char* filename = "libdlext_test_different_soname.so"; + // 1. Make sure there is no library with soname in default search path + void* handle = dlopen(soname, RTLD_NOW); + ASSERT_TRUE(handle == nullptr); + + // 2. Load a library using filename + handle = dlopen(filename, RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + // 3. Find library by soname + void* handle_soname = dlopen(soname, RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_soname != nullptr) << dlerror(); + ASSERT_EQ(handle, handle_soname); + + // 4. RTLD_NOLOAD should still work with filename + void* handle_filename = dlopen(filename, RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_filename != nullptr) << dlerror(); + ASSERT_EQ(handle, handle_filename); + + dlclose(handle_filename); + dlclose(handle_soname); + dlclose(handle); +} + // ifuncs are only supported on intel and arm64 for now #if defined (__aarch64__) || defined(__i386__) || defined(__x86_64__) TEST(dlfcn, ifunc) { diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 7ca856cdd..665ce0cb7 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -117,6 +117,17 @@ build_type := target build_target := SHARED_LIBRARY include $(TEST_PATH)/Android.build.mk +# ---------------------------------------------------------------------------- +# Library with soname which does not match filename +# ---------------------------------------------------------------------------- +libdlext_test_different_soname_src_files := \ + dlext_test_library.cpp \ + +module := libdlext_test_different_soname +module_tag := optional +libdlext_test_different_soname_ldflags := -Wl,-soname=libdlext_test_soname.so +include $(LOCAL_PATH)/Android.build.testlib.mk + # ----------------------------------------------------------------------------- # Library used by dlext tests - zipped and aligned # ----------------------------------------------------------------------------- From 2f836d4989845c0c82a1e4f99206fb0ff0d137a2 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 18 Mar 2015 14:14:02 -0700 Subject: [PATCH 011/123] Make __get_thread inlined. Bug: 19825434 Change-Id: Ifb672a45a5776b83625a25654ed0d6f7fc368ae3 --- libc/bionic/ndk_cruft.cpp | 8 ++++++++ libc/bionic/pthread_internal.h | 6 ++++-- libc/bionic/pthread_internals.cpp | 4 ---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp index 109c5239b..8b34495f8 100644 --- a/libc/bionic/ndk_cruft.cpp +++ b/libc/bionic/ndk_cruft.cpp @@ -346,6 +346,14 @@ extern "C" void* dlmalloc(size_t size) { return malloc(size); } +#define __get_thread __real_get_thread +#include "pthread_internal.h" +#undef __get_thread +// Various third-party apps contain a backport of our pthread_rwlock implementation that uses this. +extern "C" pthread_internal_t* __get_thread() { + return __real_get_thread(); +} + #endif // !defined(__LP64__) // This is never implemented in bionic, only needed for ABI compatibility with the NDK. diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 8da99dd36..99882ae98 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -113,8 +113,10 @@ __LIBC_HIDDEN__ void __init_tls(pthread_internal_t* thread); __LIBC_HIDDEN__ void __init_alternate_signal_stack(pthread_internal_t*); __LIBC_HIDDEN__ void _pthread_internal_add(pthread_internal_t* thread); -/* Various third-party apps contain a backport of our pthread_rwlock implementation that uses this. */ -extern "C" __LIBC64_HIDDEN__ pthread_internal_t* __get_thread(void); +// Make __get_thread() inlined for performance reason. See http://b/19825434. +static inline __always_inline pthread_internal_t* __get_thread() { + return reinterpret_cast(__get_tls()[TLS_SLOT_THREAD_ID]); +} __LIBC_HIDDEN__ void pthread_key_clean_all(void); __LIBC_HIDDEN__ void _pthread_internal_remove_locked(pthread_internal_t* thread, bool free_thread); diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp index 14061d1e6..0dd88fe80 100644 --- a/libc/bionic/pthread_internals.cpp +++ b/libc/bionic/pthread_internals.cpp @@ -68,7 +68,3 @@ void _pthread_internal_add(pthread_internal_t* thread) { } g_thread_list = thread; } - -pthread_internal_t* __get_thread(void) { - return reinterpret_cast(__get_tls()[TLS_SLOT_THREAD_ID]); -} From 52393a56335435efc605aff66aca3145873e1e56 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 18 Mar 2015 22:50:01 -0700 Subject: [PATCH 012/123] Move open from zip tests to dlext To avoid building them for glibc. Also replace snprintf with std::string Change-Id: I12e1d2e4ab46ff5af6c05453da67842e0d838fc5 --- tests/dlext_test.cpp | 71 +++++++++++++++++++++++++++++++------------- tests/dlfcn_test.cpp | 48 ------------------------------ 2 files changed, 50 insertions(+), 69 deletions(-) diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index d832653bd..ca6a75a95 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -52,9 +52,9 @@ typedef int (*fn)(void); #define LIBSIZE 1024*1024 // how much address space to reserve for it #if defined(__LP64__) -#define LIBPATH_PREFIX "%s/nativetest64/libdlext_test_fd/" +#define LIBPATH_PREFIX "/nativetest64/libdlext_test_fd/" #else -#define LIBPATH_PREFIX "%s/nativetest/libdlext_test_fd/" +#define LIBPATH_PREFIX "/nativetest/libdlext_test_fd/" #endif #define LIBPATH LIBPATH_PREFIX "libdlext_test_fd.so" @@ -103,16 +103,13 @@ TEST_F(DlExtTest, ExtInfoNoFlags) { } TEST_F(DlExtTest, ExtInfoUseFd) { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - char lib_path[PATH_MAX]; - snprintf(lib_path, sizeof(lib_path), LIBPATH, android_data); + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBPATH; android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD; - extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); + extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path.c_str(), O_RDONLY | O_CLOEXEC)); ASSERT_TRUE(extinfo.library_fd != -1); - handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo); + handle_ = android_dlopen_ext(lib_path.c_str(), RTLD_NOW, &extinfo); ASSERT_DL_NOTNULL(handle_); fn f = reinterpret_cast(dlsym(handle_, "getRandomNumber")); ASSERT_DL_NOTNULL(f); @@ -120,18 +117,14 @@ TEST_F(DlExtTest, ExtInfoUseFd) { } TEST_F(DlExtTest, ExtInfoUseFdWithOffset) { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - - char lib_path[PATH_MAX]; - snprintf(lib_path, sizeof(lib_path), LIBZIPPATH, android_data); + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; - extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); + extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path.c_str(), O_RDONLY | O_CLOEXEC)); extinfo.library_fd_offset = LIBZIP_OFFSET; - handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo); + handle_ = android_dlopen_ext(lib_path.c_str(), RTLD_NOW, &extinfo); ASSERT_DL_NOTNULL(handle_); fn f = reinterpret_cast(dlsym(handle_, "getRandomNumber")); @@ -140,15 +133,11 @@ TEST_F(DlExtTest, ExtInfoUseFdWithOffset) { } TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - - char lib_path[PATH_MAX]; - snprintf(lib_path, sizeof(lib_path), LIBZIPPATH, android_data); + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; - extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); + extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path.c_str(), O_RDONLY | O_CLOEXEC)); extinfo.library_fd_offset = 17; handle_ = android_dlopen_ext("libname_placeholder", RTLD_NOW, &extinfo); @@ -184,6 +173,46 @@ TEST_F(DlExtTest, ExtInfoUseOffsetWihtoutFd) { ASSERT_STREQ("dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20", dlerror()); } +TEST(dlfcn, dlopen_from_zip_absolute_path) { + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; + + void* handle = dlopen((lib_path + "!libdir/libdlext_test_fd.so").c_str(), RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + int (*fn)(void); + fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); + ASSERT_TRUE(fn != nullptr); + EXPECT_EQ(4, fn()); + + dlclose(handle); +} + +TEST(dlfcn, dlopen_from_zip_ld_library_path) { + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!libdir"; + + typedef void (*fn_t)(const char*); + fn_t android_update_LD_LIBRARY_PATH = + reinterpret_cast(dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH")); + + ASSERT_TRUE(android_update_LD_LIBRARY_PATH != nullptr) << dlerror(); + + void* handle = dlopen("libdlext_test_fd.so", RTLD_NOW); + ASSERT_TRUE(handle == nullptr); + + android_update_LD_LIBRARY_PATH(lib_path.c_str()); + + handle = dlopen("libdlext_test_fd.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + int (*fn)(void); + fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); + ASSERT_TRUE(fn != nullptr); + EXPECT_EQ(4, fn()); + + dlclose(handle); +} + + TEST_F(DlExtTest, Reserved) { void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 2bd9476fa..1061e84f2 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -29,14 +29,6 @@ #define ASSERT_SUBSTR(needle, haystack) \ ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack) -#if defined(__LP64__) -#define LIBPATH_PREFIX "/nativetest64/libdlext_test_fd/" -#else -#define LIBPATH_PREFIX "/nativetest/libdlext_test_fd/" -#endif - -#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_fd_zipaligned.zip" - static bool g_called = false; extern "C" void DlSymTestFunction() { g_called = true; @@ -878,46 +870,6 @@ TEST(dlfcn, dlopen_symlink) { dlclose(handle2); } -TEST(dlfcn, dlopen_from_zip_absolute_path) { - const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; - - void* handle = dlopen((lib_path + "!libdir/libdlext_test_fd.so").c_str(), RTLD_NOW); - ASSERT_TRUE(handle != nullptr) << dlerror(); - - int (*fn)(void); - fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); - ASSERT_TRUE(fn != nullptr); - EXPECT_EQ(4, fn()); - - dlclose(handle); -} - -TEST(dlfcn, dlopen_from_zip_ld_library_path) { - const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!libdir"; - - typedef void (*fn_t)(const char*); - fn_t android_update_LD_LIBRARY_PATH = - reinterpret_cast(dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH")); - - ASSERT_TRUE(android_update_LD_LIBRARY_PATH != nullptr) << dlerror(); - - void* handle = dlopen("libdlext_test_fd.so", RTLD_NOW); - ASSERT_TRUE(handle == nullptr); - - android_update_LD_LIBRARY_PATH(lib_path.c_str()); - - handle = dlopen("libdlext_test_fd.so", RTLD_NOW); - ASSERT_TRUE(handle != nullptr) << dlerror(); - - int (*fn)(void); - fn = reinterpret_cast(dlsym(handle, "getRandomNumber")); - ASSERT_TRUE(fn != nullptr); - EXPECT_EQ(4, fn()); - - dlclose(handle); -} - - // libtest_dlopen_from_ctor_main.so depends on // libtest_dlopen_from_ctor.so which has a constructor // that calls dlopen(libc...). This is to test the situation From aec2bb5ec67637ec4b5374287e9bc0d572632054 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 19 Mar 2015 16:28:19 -0700 Subject: [PATCH 013/123] Fix two errors in pthread_detach.cpp. The errors are introduced in "Make pthread join_state not protected by g_thread_list_lock". Bug: 19636317 Change-Id: I58ae9711da94bfbac809abfd81311eeb70301a4b --- libc/bionic/pthread_detach.cpp | 2 +- libc/bionic/pthread_internal.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index 7ae5eb4c6..9f957f496 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -45,7 +45,7 @@ int pthread_detach(pthread_t t) { switch (old_state) { case THREAD_NOT_JOINED: return 0; case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc. - case THREAD_DETACHED: return THREAD_DETACHED; + case THREAD_DETACHED: return EINVAL; case THREAD_EXITED_NOT_JOINED: break; // Call pthread_join out of scope of pthread_accessor. } } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 99882ae98..538e0daaf 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -39,9 +39,6 @@ /* Has the thread been joined by another thread? */ #define PTHREAD_ATTR_FLAG_JOINED 0x00000002 -/* Did the thread exit without freeing pthread_internal_t? */ -#define PTHREAD_ATTR_FLAG_ZOMBIE 0x00000004 - struct pthread_key_data_t { uintptr_t seq; // Use uintptr_t just for alignment, as we use pointer below. void* data; From bbb0432a33787f1a627abb396fe343a7943ac7bc Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 19 Mar 2015 15:19:25 -0700 Subject: [PATCH 014/123] Return EINVAL when calling pthread_detach for joined thread. Change-Id: I717015132187e087e0ad485284a13c8801e25e77 --- libc/bionic/pthread_detach.cpp | 2 +- tests/pthread_test.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index 9f957f496..dcdb7b135 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -44,7 +44,7 @@ int pthread_detach(pthread_t t) { } switch (old_state) { case THREAD_NOT_JOINED: return 0; - case THREAD_JOINED: return 0; // Already being joined; silently do nothing, like glibc. + case THREAD_JOINED: return EINVAL; case THREAD_DETACHED: return EINVAL; case THREAD_EXITED_NOT_JOINED: break; // Call pthread_join out of scope of pthread_accessor. } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index de60f2867..251a230e9 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -271,8 +271,11 @@ TEST(pthread, pthread_no_op_detach_after_join) { sleep(1); // (Give t2 a chance to call pthread_join.) - // ...a call to pthread_detach on thread 1 will "succeed" (silently fail)... +#if defined(__BIONIC__) + ASSERT_EQ(EINVAL, pthread_detach(t1)); +#else ASSERT_EQ(0, pthread_detach(t1)); +#endif AssertDetached(t1, false); spinhelper.UnSpin(); From e631f91a01269e9f682f9fa4a7812a2837f4185e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 19 Mar 2015 17:50:29 -0700 Subject: [PATCH 015/123] Limit soinfo compatibility fields to arm32 Change-Id: I3924cd68397c223c0d206295d587f9dd8ebdc086 --- linker/linker.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linker/linker.h b/linker/linker.h index 04acda413..05735f64b 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -149,13 +149,13 @@ struct soinfo { ElfW(Addr) base; size_t size; -#ifndef __LP64__ +#if defined(__arm__) uint32_t unused1; // DO NOT USE, maintained for compatibility. #endif ElfW(Dyn)* dynamic; -#ifndef __LP64__ +#if defined(__arm__) uint32_t unused2; // DO NOT USE, maintained for compatibility uint32_t unused3; // DO NOT USE, maintained for compatibility #endif From a2db50d5d7fa67b297eddd1c0549f08ea4b6a950 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 20 Mar 2015 10:58:04 -0700 Subject: [PATCH 016/123] Fix alignment error for pthread_internal_t/pthread stack. aligned attribute can only control compiler's behavior, but we are manually allocating pthread_internal_t. So we need to make sure of alignment manually. Change-Id: Iea4c46eadf10dfd15dc955c5f41cf6063cfd8536 --- libc/bionic/pthread_create.cpp | 11 ++++++----- libc/bionic/pthread_internal.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index a4bd054c7..5389f144f 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -162,15 +162,16 @@ static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, } // Mapped space(or user allocated stack) is used for: - // thread_internal_t + // pthread_internal_t // thread stack (including guard page) - stack_top -= sizeof(pthread_internal_t); + + // To safely access the pthread_internal_t and thread stack, we need to find a 16-byte aligned boundary. + stack_top = reinterpret_cast( + (reinterpret_cast(stack_top) - sizeof(pthread_internal_t)) & ~0xf); + pthread_internal_t* thread = reinterpret_cast(stack_top); attr->stack_size = stack_top - reinterpret_cast(attr->stack_base); - // No need to check stack_top alignment. The size of pthread_internal_t is 16-bytes aligned, - // and user allocated stack is guaranteed by pthread_attr_setstack. - thread->mmap_size = mmap_size; thread->attr = *attr; __init_tls(thread); diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 538e0daaf..13964c2d8 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -103,7 +103,7 @@ struct pthread_internal_t { */ #define __BIONIC_DLERROR_BUFFER_SIZE 512 char dlerror_buffer[__BIONIC_DLERROR_BUFFER_SIZE]; -} __attribute__((aligned(16))); // Align it as thread stack top below it should be aligned. +}; __LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread, bool add_to_thread_list); __LIBC_HIDDEN__ void __init_tls(pthread_internal_t* thread); From b58457221364eaad039c2c49a42626b725e980d5 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 16 Mar 2015 22:46:42 -0700 Subject: [PATCH 017/123] Add test for pthread types alignment check. Bug: 19249079 Change-Id: I83c4f0d11ec5d82a346ae0057d02a92bb1d519e8 --- libc/bionic/pthread_cond.cpp | 10 ++++-- libc/bionic/pthread_rwlock.cpp | 10 ++++-- libc/include/pthread.h | 4 +-- tests/pthread_test.cpp | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp index 95a433c11..4a69da558 100644 --- a/libc/bionic/pthread_cond.cpp +++ b/libc/bionic/pthread_cond.cpp @@ -120,9 +120,15 @@ struct pthread_cond_internal_t { #endif }; +static_assert(sizeof(pthread_cond_t) == sizeof(pthread_cond_internal_t), + "pthread_cond_t should actually be pthread_cond_internal_t in implementation."); + +// For binary compatibility with old version of pthread_cond_t, we can't use more strict alignment +// than 4-byte alignment. +static_assert(alignof(pthread_cond_t) == 4, + "pthread_cond_t should fulfill the alignment requirement of pthread_cond_internal_t."); + static pthread_cond_internal_t* __get_internal_cond(pthread_cond_t* cond_interface) { - static_assert(sizeof(pthread_cond_t) == sizeof(pthread_cond_internal_t), - "pthread_cond_t should actually be pthread_cond_internal_t in implementation."); return reinterpret_cast(cond_interface); } diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp index f089940a8..8aa40ae0a 100644 --- a/libc/bionic/pthread_rwlock.cpp +++ b/libc/bionic/pthread_rwlock.cpp @@ -107,9 +107,15 @@ struct pthread_rwlock_internal_t { #endif }; +static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t), + "pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation."); + +// For binary compatibility with old version of pthread_rwlock_t, we can't use more strict +// alignment than 4-byte alignment. +static_assert(alignof(pthread_rwlock_t) == 4, + "pthread_rwlock_t should fulfill the alignment requirement of pthread_rwlock_internal_t."); + static inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) { - static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t), - "pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation."); return reinterpret_cast(rwlock_interface); } diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 09ea113aa..234a43d51 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -78,7 +78,7 @@ typedef struct { #else char __private[4]; #endif -} pthread_cond_t __attribute__((aligned(sizeof(long)))); +} pthread_cond_t __attribute__((aligned(4))); #define PTHREAD_COND_INITIALIZER { { 0 } } @@ -93,7 +93,7 @@ typedef struct { #else char __private[40]; #endif -} pthread_rwlock_t __attribute__((aligned(8))); +} pthread_rwlock_t __attribute__((aligned(4))); #define PTHREAD_RWLOCK_INITIALIZER { { 0 } } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 251a230e9..4eb352d8e 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -33,6 +33,7 @@ #include #include +#include TEST(pthread, pthread_key_create) { pthread_key_t key; @@ -1303,3 +1304,60 @@ TEST(pthread, pthread_mutex_owner_tid_limit) { // Change the implementation if we need to support higher value than 65535. ASSERT_LE(pid_max, 65536); } + +class StrictAlignmentAllocator { + public: + void* allocate(size_t size, size_t alignment) { + char* p = new char[size + alignment * 2]; + allocated_array.push_back(p); + while (!is_strict_aligned(p, alignment)) { + ++p; + } + return p; + } + + ~StrictAlignmentAllocator() { + for (auto& p : allocated_array) { + delete [] p; + } + } + + private: + bool is_strict_aligned(char* p, size_t alignment) { + return (reinterpret_cast(p) % (alignment * 2)) == alignment; + } + + std::vector allocated_array; +}; + +TEST(pthread, pthread_types_allow_four_bytes_alignment) { +#if defined(__BIONIC__) + // For binary compatibility with old version, we need to allow 4-byte aligned data for pthread types. + StrictAlignmentAllocator allocator; + pthread_mutex_t* mutex = reinterpret_cast( + allocator.allocate(sizeof(pthread_mutex_t), 4)); + ASSERT_EQ(0, pthread_mutex_init(mutex, NULL)); + ASSERT_EQ(0, pthread_mutex_lock(mutex)); + ASSERT_EQ(0, pthread_mutex_unlock(mutex)); + ASSERT_EQ(0, pthread_mutex_destroy(mutex)); + + pthread_cond_t* cond = reinterpret_cast( + allocator.allocate(sizeof(pthread_cond_t), 4)); + ASSERT_EQ(0, pthread_cond_init(cond, NULL)); + ASSERT_EQ(0, pthread_cond_signal(cond)); + ASSERT_EQ(0, pthread_cond_broadcast(cond)); + ASSERT_EQ(0, pthread_cond_destroy(cond)); + + pthread_rwlock_t* rwlock = reinterpret_cast( + allocator.allocate(sizeof(pthread_rwlock_t), 4)); + ASSERT_EQ(0, pthread_rwlock_init(rwlock, NULL)); + ASSERT_EQ(0, pthread_rwlock_rdlock(rwlock)); + ASSERT_EQ(0, pthread_rwlock_unlock(rwlock)); + ASSERT_EQ(0, pthread_rwlock_wrlock(rwlock)); + ASSERT_EQ(0, pthread_rwlock_unlock(rwlock)); + ASSERT_EQ(0, pthread_rwlock_destroy(rwlock)); + +#else + GTEST_LOG_(INFO) << "This test tests bionic implementation details."; +#endif +} From c889f13493e3ae5b6a75b59a575a7d2bffbd6481 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 23 Mar 2015 13:29:15 -0700 Subject: [PATCH 018/123] Work around a bug in the Jenkins queue API. https://issues.jenkins-ci.org/browse/JENKINS-27256 1.601 broke the ability to get a build's URL before the build had actually started. The bug is pseudo-fixed, but would require installing a new plugin and fixing the python jenkinsapi plugin for the time being to use /queuefix/ rather than /queue/. Just avoiding logging a URL for now. Change-Id: Ibf90e5887fc4532dac688082ad7729787897da11 --- tools/bionicbb/gmail_listener.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py index 0cd31c9bf..770f0c414 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/gmail_listener.py @@ -190,8 +190,10 @@ def build_project(gerrit_info, dry_run, lunch_target=None): if lunch_target is not None: params['LUNCH_TARGET'] = lunch_target if not dry_run: - job = jenkins[build].invoke(build_params=params) - url = job.get_build().baseurl + _ = jenkins[build].invoke(build_params=params) + # https://issues.jenkins-ci.org/browse/JENKINS-27256 + # url = job.get_build().baseurl + url = 'URL UNAVAILABLE' else: url = 'DRY_RUN_URL' print '{}({}): {} => {} {} {}'.format( From 84c10c2e7928cf2128f2495b9d5ed916088b2f2b Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 23 Mar 2015 14:58:45 -0700 Subject: [PATCH 019/123] RTLD_LAZY is not supported, use RTLD_NOW instead. Change-Id: Ia08ed6416aef686435224b50b3e58955d09f04e2 --- libc/bionic/NetdClient.cpp | 2 +- libc/bionic/malloc_debug_common.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libc/bionic/NetdClient.cpp b/libc/bionic/NetdClient.cpp index 5b0f4fdde..b117d72a9 100644 --- a/libc/bionic/NetdClient.cpp +++ b/libc/bionic/NetdClient.cpp @@ -34,7 +34,7 @@ static void netdClientInitFunction(void* handle, const char* symbol, FunctionTyp } static void netdClientInitImpl() { - void* netdClientHandle = dlopen("libnetd_client.so", RTLD_LAZY); + void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW); if (netdClientHandle == NULL) { // If the library is not available, it's not an error. We'll just use // default implementations of functions that it would've overridden. diff --git a/libc/bionic/malloc_debug_common.cpp b/libc/bionic/malloc_debug_common.cpp index 1a2765a60..ee796c6dc 100644 --- a/libc/bionic/malloc_debug_common.cpp +++ b/libc/bionic/malloc_debug_common.cpp @@ -402,7 +402,7 @@ static void malloc_init_impl() { } // Load .so that implements the required malloc debugging functionality. - void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY); + void* malloc_impl_handle = dlopen(so_name, RTLD_NOW); if (malloc_impl_handle == NULL) { error_log("%s: Missing module %s required for malloc debug level %d: %s", getprogname(), so_name, g_malloc_debug_level, dlerror()); From 673b15e4ee2c6d99b150aedddc0f389e29f98e1b Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 19 Mar 2015 14:19:19 -0700 Subject: [PATCH 020/123] Let g_thread_list_lock only protect g_thread_list. As glibc/netbsd don't protect access to thread struct members by a global lock, we don't want to do it either. This change reduces the responsibility of g_thread_list_lock to only protect g_thread_list. Bug: 19636317 Change-Id: I897890710653dac165d8fa4452c7ecf74abdbf2b --- libc/Android.mk | 2 +- libc/bionic/libc_init_common.cpp | 4 +- libc/bionic/pthread_accessor.h | 64 ------------------- libc/bionic/pthread_create.cpp | 10 +-- libc/bionic/pthread_detach.cpp | 35 +++++----- libc/bionic/pthread_exit.cpp | 4 +- libc/bionic/pthread_getcpuclockid.cpp | 6 +- libc/bionic/pthread_getschedparam.cpp | 6 +- ...ead_internals.cpp => pthread_internal.cpp} | 60 +++++++++++------ libc/bionic/pthread_internal.h | 12 ++-- libc/bionic/pthread_join.cpp | 44 ++++++------- libc/bionic/pthread_kill.cpp | 17 ++--- libc/bionic/pthread_setname_np.cpp | 15 ++--- libc/bionic/pthread_setschedparam.cpp | 6 +- 14 files changed, 107 insertions(+), 178 deletions(-) delete mode 100644 libc/bionic/pthread_accessor.h rename libc/bionic/{pthread_internals.cpp => pthread_internal.cpp} (70%) diff --git a/libc/Android.mk b/libc/Android.mk index 7bbdd9901..6f430ccfc 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -521,7 +521,7 @@ libc_pthread_src_files := \ bionic/pthread_getcpuclockid.cpp \ bionic/pthread_getschedparam.cpp \ bionic/pthread_gettid_np.cpp \ - bionic/pthread_internals.cpp \ + bionic/pthread_internal.cpp \ bionic/pthread_join.cpp \ bionic/pthread_key.cpp \ bionic/pthread_kill.cpp \ diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp index 52ca0f228..36dc0854b 100644 --- a/libc/bionic/libc_init_common.cpp +++ b/libc/bionic/libc_init_common.cpp @@ -92,7 +92,7 @@ void __libc_init_tls(KernelArgumentBlock& args) { main_thread.attr.stack_size = 0; // User code should never see this; we'll compute it when asked. // TODO: the main thread's sched_policy and sched_priority need to be queried. - __init_thread(&main_thread, false); + __init_thread(&main_thread); __init_tls(&main_thread); __set_tls(main_thread.tls); main_thread.tls[TLS_SLOT_BIONIC_PREINIT] = &args; @@ -113,7 +113,7 @@ void __libc_init_common(KernelArgumentBlock& args) { // Get the main thread from TLS and add it to the thread list. pthread_internal_t* main_thread = __get_thread(); - _pthread_internal_add(main_thread); + __pthread_internal_add(main_thread); __system_properties_init(); // Requires 'environ'. diff --git a/libc/bionic/pthread_accessor.h b/libc/bionic/pthread_accessor.h deleted file mode 100644 index df4a5a22b..000000000 --- a/libc/bionic/pthread_accessor.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PTHREAD_ACCESSOR_H -#define PTHREAD_ACCESSOR_H - -#include - -#include "private/bionic_macros.h" -#include "pthread_internal.h" - -class pthread_accessor { - public: - explicit pthread_accessor(pthread_t desired_thread) { - Lock(); - for (thread_ = g_thread_list; thread_ != NULL; thread_ = thread_->next) { - if (thread_ == reinterpret_cast(desired_thread)) { - break; - } - } - } - - ~pthread_accessor() { - Unlock(); - } - - void Unlock() { - if (is_locked_) { - is_locked_ = false; - thread_ = NULL; - pthread_mutex_unlock(&g_thread_list_lock); - } - } - - pthread_internal_t& operator*() const { return *thread_; } - pthread_internal_t* operator->() const { return thread_; } - pthread_internal_t* get() const { return thread_; } - - private: - pthread_internal_t* thread_; - bool is_locked_; - - void Lock() { - pthread_mutex_lock(&g_thread_list_lock); - is_locked_ = true; - } - - DISALLOW_COPY_AND_ASSIGN(pthread_accessor); -}; - -#endif // PTHREAD_ACCESSOR_H diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index a4bd054c7..ef3ce0572 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -83,7 +83,7 @@ void __init_alternate_signal_stack(pthread_internal_t* thread) { } } -int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) { +int __init_thread(pthread_internal_t* thread) { int error = 0; if (__predict_true((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) == 0)) { @@ -108,10 +108,6 @@ int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) { thread->cleanup_stack = NULL; - if (add_to_thread_list) { - _pthread_internal_add(thread); - } - return error; } @@ -265,7 +261,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, return clone_errno; } - int init_errno = __init_thread(thread, true); + int init_errno = __init_thread(thread); if (init_errno != 0) { // Mark the thread detached and replace its start_routine with a no-op. // Letting the thread run is the easiest way to clean up its resources. @@ -276,7 +272,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, } // Publish the pthread_t and unlock the mutex to let the new thread start running. - *thread_out = reinterpret_cast(thread); + *thread_out = __pthread_internal_add(thread); pthread_mutex_unlock(&thread->startup_handshake_mutex); return 0; diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index dcdb7b135..fb8e0dd01 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -29,27 +29,24 @@ #include #include -#include "pthread_accessor.h" +#include "pthread_internal.h" int pthread_detach(pthread_t t) { - { - pthread_accessor thread(t); - if (thread.get() == NULL) { - return ESRCH; - } - - ThreadJoinState old_state = THREAD_NOT_JOINED; - while (old_state == THREAD_NOT_JOINED && - !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_DETACHED)) { - } - switch (old_state) { - case THREAD_NOT_JOINED: return 0; - case THREAD_JOINED: return EINVAL; - case THREAD_DETACHED: return EINVAL; - case THREAD_EXITED_NOT_JOINED: break; // Call pthread_join out of scope of pthread_accessor. - } + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { + return ESRCH; } - // The thread is in THREAD_EXITED_NOT_JOINED, use pthread_join to clean it up. - return pthread_join(t, NULL); + ThreadJoinState old_state = THREAD_NOT_JOINED; + while (old_state == THREAD_NOT_JOINED && + !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_DETACHED)) { + } + + if (old_state == THREAD_NOT_JOINED) { + return 0; + } else if (old_state == THREAD_EXITED_NOT_JOINED) { + // Use pthread_join to clean it up. + return pthread_join(t, NULL); + } + return EINVAL; } diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index 81cc67be9..c2232a939 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -100,9 +100,7 @@ void pthread_exit(void* return_value) { __set_tid_address(NULL); // pthread_internal_t is freed below with stack, not here. - pthread_mutex_lock(&g_thread_list_lock); - _pthread_internal_remove_locked(thread, false); - pthread_mutex_unlock(&g_thread_list_lock); + __pthread_internal_remove(thread); if (thread->mmap_size != 0) { // We need to free mapped space for detached threads when they exit. diff --git a/libc/bionic/pthread_getcpuclockid.cpp b/libc/bionic/pthread_getcpuclockid.cpp index d11f56a2a..2bf200480 100644 --- a/libc/bionic/pthread_getcpuclockid.cpp +++ b/libc/bionic/pthread_getcpuclockid.cpp @@ -28,11 +28,11 @@ #include -#include "pthread_accessor.h" +#include "pthread_internal.h" int pthread_getcpuclockid(pthread_t t, clockid_t* clockid) { - pthread_accessor thread(t); - if (thread.get() == NULL) { + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { return ESRCH; } diff --git a/libc/bionic/pthread_getschedparam.cpp b/libc/bionic/pthread_getschedparam.cpp index 2cdc11a4c..052fb05f9 100644 --- a/libc/bionic/pthread_getschedparam.cpp +++ b/libc/bionic/pthread_getschedparam.cpp @@ -29,13 +29,13 @@ #include #include "private/ErrnoRestorer.h" -#include "pthread_accessor.h" +#include "pthread_internal.h" int pthread_getschedparam(pthread_t t, int* policy, sched_param* param) { ErrnoRestorer errno_restorer; - pthread_accessor thread(t); - if (thread.get() == NULL) { + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { return ESRCH; } diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internal.cpp similarity index 70% rename from libc/bionic/pthread_internals.cpp rename to libc/bionic/pthread_internal.cpp index 0dd88fe80..1967ccf8c 100644 --- a/libc/bionic/pthread_internals.cpp +++ b/libc/bionic/pthread_internal.cpp @@ -38,26 +38,10 @@ #include "private/libc_logging.h" #include "private/ScopedPthreadMutexLocker.h" -pthread_internal_t* g_thread_list = NULL; -pthread_mutex_t g_thread_list_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_internal_t* g_thread_list = NULL; +static pthread_mutex_t g_thread_list_lock = PTHREAD_MUTEX_INITIALIZER; -void _pthread_internal_remove_locked(pthread_internal_t* thread, bool free_thread) { - if (thread->next != NULL) { - thread->next->prev = thread->prev; - } - if (thread->prev != NULL) { - thread->prev->next = thread->next; - } else { - g_thread_list = thread->next; - } - - if (free_thread && thread->mmap_size != 0) { - // Free mapped space, including thread stack and pthread_internal_t. - munmap(thread->attr.stack_base, thread->mmap_size); - } -} - -void _pthread_internal_add(pthread_internal_t* thread) { +pthread_t __pthread_internal_add(pthread_internal_t* thread) { ScopedPthreadMutexLocker locker(&g_thread_list_lock); // We insert at the head. @@ -67,4 +51,42 @@ void _pthread_internal_add(pthread_internal_t* thread) { thread->next->prev = thread; } g_thread_list = thread; + return reinterpret_cast(thread); +} + +void __pthread_internal_remove(pthread_internal_t* thread) { + ScopedPthreadMutexLocker locker(&g_thread_list_lock); + + if (thread->next != NULL) { + thread->next->prev = thread->prev; + } + if (thread->prev != NULL) { + thread->prev->next = thread->next; + } else { + g_thread_list = thread->next; + } +} + +static void __pthread_internal_free(pthread_internal_t* thread) { + if (thread->mmap_size != 0) { + // Free mapped space, including thread stack and pthread_internal_t. + munmap(thread->attr.stack_base, thread->mmap_size); + } +} + +void __pthread_internal_remove_and_free(pthread_internal_t* thread) { + __pthread_internal_remove(thread); + __pthread_internal_free(thread); +} + +pthread_internal_t* __pthread_internal_find(pthread_t thread_id) { + pthread_internal_t* thread = reinterpret_cast(thread_id); + ScopedPthreadMutexLocker locker(&g_thread_list_lock); + + for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) { + if (t == thread) { + return thread; + } + } + return NULL; } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 538e0daaf..99c455e7b 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -105,10 +105,14 @@ struct pthread_internal_t { char dlerror_buffer[__BIONIC_DLERROR_BUFFER_SIZE]; } __attribute__((aligned(16))); // Align it as thread stack top below it should be aligned. -__LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread, bool add_to_thread_list); +__LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread); __LIBC_HIDDEN__ void __init_tls(pthread_internal_t* thread); __LIBC_HIDDEN__ void __init_alternate_signal_stack(pthread_internal_t*); -__LIBC_HIDDEN__ void _pthread_internal_add(pthread_internal_t* thread); + +__LIBC_HIDDEN__ pthread_t __pthread_internal_add(pthread_internal_t* thread); +__LIBC_HIDDEN__ pthread_internal_t* __pthread_internal_find(pthread_t pthread_id); +__LIBC_HIDDEN__ void __pthread_internal_remove(pthread_internal_t* thread); +__LIBC_HIDDEN__ void __pthread_internal_remove_and_free(pthread_internal_t* thread); // Make __get_thread() inlined for performance reason. See http://b/19825434. static inline __always_inline pthread_internal_t* __get_thread() { @@ -116,7 +120,6 @@ static inline __always_inline pthread_internal_t* __get_thread() { } __LIBC_HIDDEN__ void pthread_key_clean_all(void); -__LIBC_HIDDEN__ void _pthread_internal_remove_locked(pthread_internal_t* thread, bool free_thread); /* * Traditionally we gave threads a 1MiB stack. When we started @@ -127,9 +130,6 @@ __LIBC_HIDDEN__ void _pthread_internal_remove_locked(pthread_internal_t* thread, */ #define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ) -__LIBC_HIDDEN__ extern pthread_internal_t* g_thread_list; -__LIBC_HIDDEN__ extern pthread_mutex_t g_thread_list_lock; - /* Needed by fork. */ __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare(); __LIBC_HIDDEN__ extern void __bionic_atfork_run_child(); diff --git a/libc/bionic/pthread_join.cpp b/libc/bionic/pthread_join.cpp index 15543b48e..4d852cb4d 100644 --- a/libc/bionic/pthread_join.cpp +++ b/libc/bionic/pthread_join.cpp @@ -29,35 +29,31 @@ #include #include "private/bionic_futex.h" -#include "pthread_accessor.h" +#include "pthread_internal.h" int pthread_join(pthread_t t, void** return_value) { if (t == pthread_self()) { return EDEADLK; } - pid_t tid; - volatile int* tid_ptr; - { - pthread_accessor thread(t); - if (thread.get() == NULL) { - return ESRCH; - } - - ThreadJoinState old_state = THREAD_NOT_JOINED; - while ((old_state == THREAD_NOT_JOINED || old_state == THREAD_EXITED_NOT_JOINED) && - !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_JOINED)) { - } - - if (old_state == THREAD_DETACHED || old_state == THREAD_JOINED) { - return EINVAL; - } - - tid = thread->tid; - tid_ptr = &thread->tid; + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { + return ESRCH; } - // We set the PTHREAD_ATTR_FLAG_JOINED flag with the lock held, + ThreadJoinState old_state = THREAD_NOT_JOINED; + while ((old_state == THREAD_NOT_JOINED || old_state == THREAD_EXITED_NOT_JOINED) && + !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_JOINED)) { + } + + if (old_state == THREAD_DETACHED || old_state == THREAD_JOINED) { + return EINVAL; + } + + pid_t tid = thread->tid; + volatile int* tid_ptr = &thread->tid; + + // We set thread->join_state to THREAD_JOINED with atomic operation, // so no one is going to remove this thread except us. // Wait for the thread to actually exit, if it hasn't already. @@ -65,14 +61,10 @@ int pthread_join(pthread_t t, void** return_value) { __futex_wait(tid_ptr, tid, NULL); } - // Take the lock again so we can pull the thread's return value - // and remove the thread from the list. - pthread_accessor thread(t); - if (return_value) { *return_value = thread->return_value; } - _pthread_internal_remove_locked(thread.get(), true); + __pthread_internal_remove_and_free(thread); return 0; } diff --git a/libc/bionic/pthread_kill.cpp b/libc/bionic/pthread_kill.cpp index 163317efc..93513fac3 100644 --- a/libc/bionic/pthread_kill.cpp +++ b/libc/bionic/pthread_kill.cpp @@ -30,26 +30,17 @@ #include #include "private/ErrnoRestorer.h" -#include "pthread_accessor.h" +#include "pthread_internal.h" extern "C" int tgkill(int tgid, int tid, int sig); int pthread_kill(pthread_t t, int sig) { ErrnoRestorer errno_restorer; - pthread_accessor thread(t); - if (thread.get() == NULL) { + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { return ESRCH; } - // There's a race here, but it's one we share with all other C libraries. - pid_t tid = thread->tid; - thread.Unlock(); - - int rc = tgkill(getpid(), tid, sig); - if (rc == -1) { - return errno; - } - - return 0; + return (tgkill(getpid(), thread->tid, sig) == -1) ? errno : 0; } diff --git a/libc/bionic/pthread_setname_np.cpp b/libc/bionic/pthread_setname_np.cpp index c4e9fb85c..bb1114e3d 100644 --- a/libc/bionic/pthread_setname_np.cpp +++ b/libc/bionic/pthread_setname_np.cpp @@ -36,9 +36,8 @@ #include #include -#include "pthread_accessor.h" -#include "pthread_internal.h" #include "private/ErrnoRestorer.h" +#include "pthread_internal.h" // This value is not exported by kernel headers. #define MAX_TASK_COMM_LEN 16 @@ -58,14 +57,12 @@ int pthread_setname_np(pthread_t t, const char* thread_name) { } // We have to change another thread's name. - pid_t tid = 0; - { - pthread_accessor thread(t); - if (thread.get() == NULL) { - return ENOENT; - } - tid = thread->tid; + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { + return ENOENT; } + pid_t tid = thread->tid; + char comm_name[sizeof(TASK_COMM_FMT) + 8]; snprintf(comm_name, sizeof(comm_name), TASK_COMM_FMT, tid); int fd = open(comm_name, O_CLOEXEC | O_WRONLY); diff --git a/libc/bionic/pthread_setschedparam.cpp b/libc/bionic/pthread_setschedparam.cpp index 419cc6f81..0ad68bb38 100644 --- a/libc/bionic/pthread_setschedparam.cpp +++ b/libc/bionic/pthread_setschedparam.cpp @@ -29,13 +29,13 @@ #include #include "private/ErrnoRestorer.h" -#include "pthread_accessor.h" +#include "pthread_internal.h" int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) { ErrnoRestorer errno_restorer; - pthread_accessor thread(t); - if (thread.get() == NULL) { + pthread_internal_t* thread = __pthread_internal_find(t); + if (thread == NULL) { return ESRCH; } From 7d57623755e68c2310c2c87ada31128b9b1989b9 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Tue, 24 Mar 2015 11:43:55 -0700 Subject: [PATCH 021/123] Convert result of a call to JSON. I still had the service that was relying on this running on top of local changes, so this hadn't been a problem yet. Change-Id: I63b45b8c7cf81972dbb7128013c1c777a2342d4c --- tools/bionicbb/gerrit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bionicbb/gerrit.py b/tools/bionicbb/gerrit.py index 76e42b421..40719b48a 100644 --- a/tools/bionicbb/gerrit.py +++ b/tools/bionicbb/gerrit.py @@ -62,8 +62,8 @@ def get_labels(change_id, patch_set): } } """ - details = call('/changes/{}/revisions/{}/review'.format( - change_id, patch_set)) + details = json.loads(call('/changes/{}/revisions/{}/review'.format( + change_id, patch_set))) labels = {'Code-Review': {}, 'Verified': {}} for review in details['labels']['Code-Review']['all']: if 'value' in review and 'email' in review: From d165f56fb69f39e41bc2c952bf51c3eb3b127d2e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 23 Mar 2015 18:43:02 -0700 Subject: [PATCH 022/123] Replace fixed arrays with vectors This effectively removes limit on length and count of LD_LIBRARY_PATH and LD_PRELOAD entries. Change-Id: Ie7ea34a50d99c4018f9dd1a33aaebc4049a7f424 --- linker/linker.cpp | 122 ++++++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 44ae4d49d..8e8ba841d 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -40,6 +40,8 @@ #include #include +#include +#include // Private C library headers. #include "private/bionic_tls.h" @@ -98,19 +100,10 @@ static const char* const kDefaultLdPaths[] = { nullptr }; -#define LDPATH_BUFSIZE (LDPATH_MAX*64) -#define LDPATH_MAX 8 +static std::vector g_ld_library_paths; +static std::vector g_ld_preload_names; -#define LDPRELOAD_BUFSIZE (LDPRELOAD_MAX*64) -#define LDPRELOAD_MAX 8 - -static char g_ld_library_paths_buffer[LDPATH_BUFSIZE]; -static const char* g_ld_library_paths[LDPATH_MAX + 1]; - -static char g_ld_preloads_buffer[LDPRELOAD_BUFSIZE]; -static const char* g_ld_preload_names[LDPRELOAD_MAX + 1]; - -static soinfo* g_ld_preloads[LDPRELOAD_MAX + 1]; +static std::vector g_ld_preloads; __LIBC_HIDDEN__ int g_ld_debug_verbosity; @@ -295,39 +288,36 @@ static void soinfo_free(soinfo* si) { } static void parse_path(const char* path, const char* delimiters, - const char** array, char* buf, size_t buf_size, size_t max_count) { + std::vector* paths) { if (path == nullptr) { return; } - size_t len = strlcpy(buf, path, buf_size); + paths->clear(); - size_t i = 0; - char* buf_p = buf; - while (i < max_count && (array[i] = strsep(&buf_p, delimiters))) { - if (*array[i] != '\0') { - ++i; + for (const char *p = path; ; ++p) { + size_t len = strcspn(p, delimiters); + // skip empty tokens + if (len == 0) { + continue; } - } - // Forget the last path if we had to truncate; this occurs if the 2nd to - // last char isn't '\0' (i.e. wasn't originally a delimiter). - if (i > 0 && len >= buf_size && buf[buf_size - 2] != '\0') { - array[i - 1] = nullptr; - } else { - array[i] = nullptr; + paths->push_back(std::string(p, len)); + p += len; + + if (*p == '\0') { + break; + } } } static void parse_LD_LIBRARY_PATH(const char* path) { - parse_path(path, ":", g_ld_library_paths, - g_ld_library_paths_buffer, sizeof(g_ld_library_paths_buffer), LDPATH_MAX); + parse_path(path, ":", &g_ld_library_paths); } static void parse_LD_PRELOAD(const char* path) { // We have historically supported ':' as well as ' ' in LD_PRELOAD. - parse_path(path, " :", g_ld_preload_names, - g_ld_preloads_buffer, sizeof(g_ld_preloads_buffer), LDPRELOAD_MAX); + parse_path(path, " :", &g_ld_preload_names); } #if defined(__arm__) @@ -883,23 +873,43 @@ static int open_library_in_zipfile(const char* const path, return fd; } -static int open_library_on_path(const char* name, - const char* const paths[], - off64_t* file_offset) { - char buf[512]; - int fd = -1; +static bool format_path(char* buf, size_t buf_size, const char* path, const char* name) { + int n = __libc_format_buffer(buf, buf_size, "%s/%s", path, name); + if (n < 0 || n >= static_cast(buf_size)) { + PRINT("Warning: ignoring very long library path: %s/%s", path, name); + return false; + } - for (size_t i = 0; paths[i] != nullptr && fd == -1; ++i) { - const char* const path = paths[i]; - int n = __libc_format_buffer(buf, sizeof(buf), "%s/%s", path, name); - if (n < 0 || n >= static_cast(sizeof(buf))) { - PRINT("Warning: ignoring very long library path: %s/%s", path, name); - return -1; + return true; +} + +static int open_library_on_default_path(const char* name, off64_t* file_offset) { + for (size_t i = 0; kDefaultLdPaths[i] != nullptr; ++i) { + char buf[512]; + if(!format_path(buf, sizeof(buf), kDefaultLdPaths[i], name)) { + continue; } - const char* separator = strchr(path, '!'); + int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + *file_offset = 0; + return fd; + } + } - if (separator != nullptr) { + return -1; +} + +static int open_library_on_ld_library_path(const char* name, off64_t* file_offset) { + for (const auto& path_str : g_ld_library_paths) { + char buf[512]; + const char* const path = path_str.c_str(); + if (!format_path(buf, sizeof(buf), path, name)) { + continue; + } + + int fd = -1; + if (strchr(buf, '!') != nullptr) { fd = open_library_in_zipfile(buf, file_offset); } @@ -909,9 +919,13 @@ static int open_library_on_path(const char* name, *file_offset = 0; } } + + if (fd != -1) { + return fd; + } } - return fd; + return -1; } static int open_library(const char* name, off64_t* file_offset) { @@ -934,9 +948,9 @@ static int open_library(const char* name, off64_t* file_offset) { } // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths. - int fd = open_library_on_path(name, g_ld_library_paths, file_offset); + int fd = open_library_on_ld_library_path(name, file_offset); if (fd == -1) { - fd = open_library_on_path(name, kDefaultLdPaths, file_offset); + fd = open_library_on_default_path(name, file_offset); } return fd; } @@ -1086,8 +1100,9 @@ static soinfo::soinfo_list_t make_global_group() { return global_group; } -static bool find_libraries(soinfo* start_with, const char* const library_names[], size_t library_names_count, soinfo* soinfos[], - soinfo* ld_preloads[], size_t ld_preloads_count, int rtld_flags, const android_dlextinfo* extinfo) { +static bool find_libraries(soinfo* start_with, const char* const library_names[], + size_t library_names_count, soinfo* soinfos[], std::vector* ld_preloads, + size_t ld_preloads_count, int rtld_flags, const android_dlextinfo* extinfo) { // Step 0: prepare. LoadTaskList load_tasks; for (size_t i = 0; i < library_names_count; ++i) { @@ -1150,7 +1165,7 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[] // for this run because they are going to appear in the local // group in the correct order. si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); - ld_preloads[soinfos_count] = si; + ld_preloads->push_back(si); } if (soinfos_count < library_names_count) { @@ -2674,8 +2689,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( StringLinkedList needed_library_name_list; size_t needed_libraries_count = 0; size_t ld_preloads_count = 0; - while (g_ld_preload_names[ld_preloads_count] != nullptr) { - needed_library_name_list.push_back(g_ld_preload_names[ld_preloads_count++]); + + for (const auto& ld_preload_name : g_ld_preload_names) { + needed_library_name_list.push_back(ld_preload_name.c_str()); ++needed_libraries_count; } @@ -2689,7 +2705,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( memset(needed_library_names, 0, sizeof(needed_library_names)); needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count); - if (needed_libraries_count > 0 && !find_libraries(si, needed_library_names, needed_libraries_count, nullptr, g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr)) { + if (needed_libraries_count > 0 && + !find_libraries(si, needed_library_names, needed_libraries_count, nullptr, + &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr)) { __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer()); exit(EXIT_FAILURE); } else if (needed_libraries_count == 0) { From fa5faa0ce6deba5ad9c96fe5cf74e63e4a6edfb3 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 24 Mar 2015 16:50:46 -0700 Subject: [PATCH 023/123] Make gensyscalls.py use the ALIAS_SYMBOL macro. Change-Id: Ib94c0abb6fc85126ecc5ed3f1962b2b8b90b9952 --- libc/arch-arm/syscalls/_exit.S | 3 +-- libc/arch-arm/syscalls/fstat64.S | 3 +-- libc/arch-arm/syscalls/fstatat64.S | 3 +-- libc/arch-arm64/syscalls/_exit.S | 3 +-- libc/arch-arm64/syscalls/fallocate.S | 3 +-- libc/arch-arm64/syscalls/fstat64.S | 3 +-- libc/arch-arm64/syscalls/fstatat64.S | 3 +-- libc/arch-arm64/syscalls/ftruncate.S | 3 +-- libc/arch-arm64/syscalls/getrlimit.S | 3 +-- libc/arch-arm64/syscalls/lseek.S | 3 +-- libc/arch-arm64/syscalls/mmap.S | 3 +-- libc/arch-arm64/syscalls/pread64.S | 3 +-- libc/arch-arm64/syscalls/prlimit64.S | 3 +-- libc/arch-arm64/syscalls/pwrite64.S | 3 +-- libc/arch-arm64/syscalls/sendfile.S | 3 +-- libc/arch-arm64/syscalls/setrlimit.S | 3 +-- libc/arch-arm64/syscalls/truncate.S | 3 +-- libc/arch-mips/syscalls/_exit.S | 3 +-- libc/arch-mips/syscalls/fstat64.S | 3 +-- libc/arch-mips/syscalls/fstatat64.S | 3 +-- libc/arch-mips64/syscalls/_exit.S | 3 +-- libc/arch-mips64/syscalls/fallocate.S | 3 +-- libc/arch-mips64/syscalls/ftruncate.S | 3 +-- libc/arch-mips64/syscalls/getrlimit.S | 3 +-- libc/arch-mips64/syscalls/lseek.S | 3 +-- libc/arch-mips64/syscalls/mmap.S | 3 +-- libc/arch-mips64/syscalls/pread64.S | 3 +-- libc/arch-mips64/syscalls/prlimit64.S | 3 +-- libc/arch-mips64/syscalls/pwrite64.S | 3 +-- libc/arch-mips64/syscalls/sendfile.S | 3 +-- libc/arch-mips64/syscalls/setrlimit.S | 3 +-- libc/arch-mips64/syscalls/truncate.S | 3 +-- libc/arch-x86/syscalls/_exit.S | 3 +-- libc/arch-x86/syscalls/fstat64.S | 3 +-- libc/arch-x86/syscalls/fstatat64.S | 3 +-- libc/arch-x86_64/syscalls/_exit.S | 3 +-- libc/arch-x86_64/syscalls/fallocate.S | 3 +-- libc/arch-x86_64/syscalls/fstat64.S | 3 +-- libc/arch-x86_64/syscalls/fstatat64.S | 3 +-- libc/arch-x86_64/syscalls/ftruncate.S | 3 +-- libc/arch-x86_64/syscalls/getrlimit.S | 3 +-- libc/arch-x86_64/syscalls/lseek.S | 3 +-- libc/arch-x86_64/syscalls/mmap.S | 3 +-- libc/arch-x86_64/syscalls/pread64.S | 3 +-- libc/arch-x86_64/syscalls/prlimit64.S | 3 +-- libc/arch-x86_64/syscalls/pwrite64.S | 3 +-- libc/arch-x86_64/syscalls/sendfile.S | 3 +-- libc/arch-x86_64/syscalls/setrlimit.S | 3 +-- libc/arch-x86_64/syscalls/truncate.S | 3 +-- libc/tools/gensyscalls.py | 8 +------- 50 files changed, 50 insertions(+), 105 deletions(-) diff --git a/libc/arch-arm/syscalls/_exit.S b/libc/arch-arm/syscalls/_exit.S index 328a5ce0b..77da74356 100644 --- a/libc/arch-arm/syscalls/_exit.S +++ b/libc/arch-arm/syscalls/_exit.S @@ -13,5 +13,4 @@ ENTRY(_exit) b __set_errno_internal END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-arm/syscalls/fstat64.S b/libc/arch-arm/syscalls/fstat64.S index c60e7eee5..798bba7f6 100644 --- a/libc/arch-arm/syscalls/fstat64.S +++ b/libc/arch-arm/syscalls/fstat64.S @@ -13,5 +13,4 @@ ENTRY(fstat64) b __set_errno_internal END(fstat64) - .globl fstat - .equ fstat, fstat64 +ALIAS_SYMBOL(fstat, fstat64) diff --git a/libc/arch-arm/syscalls/fstatat64.S b/libc/arch-arm/syscalls/fstatat64.S index ce56c36fd..03e005299 100644 --- a/libc/arch-arm/syscalls/fstatat64.S +++ b/libc/arch-arm/syscalls/fstatat64.S @@ -13,5 +13,4 @@ ENTRY(fstatat64) b __set_errno_internal END(fstatat64) - .globl fstatat - .equ fstatat, fstatat64 +ALIAS_SYMBOL(fstatat, fstatat64) diff --git a/libc/arch-arm64/syscalls/_exit.S b/libc/arch-arm64/syscalls/_exit.S index edf6744a8..d50f38d06 100644 --- a/libc/arch-arm64/syscalls/_exit.S +++ b/libc/arch-arm64/syscalls/_exit.S @@ -13,5 +13,4 @@ ENTRY(_exit) ret END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-arm64/syscalls/fallocate.S b/libc/arch-arm64/syscalls/fallocate.S index ef3d4a4c4..d42a0baa4 100644 --- a/libc/arch-arm64/syscalls/fallocate.S +++ b/libc/arch-arm64/syscalls/fallocate.S @@ -13,5 +13,4 @@ ENTRY(fallocate) ret END(fallocate) - .globl fallocate64 - .equ fallocate64, fallocate +ALIAS_SYMBOL(fallocate64, fallocate) diff --git a/libc/arch-arm64/syscalls/fstat64.S b/libc/arch-arm64/syscalls/fstat64.S index 85a07f5bd..f7c9f5447 100644 --- a/libc/arch-arm64/syscalls/fstat64.S +++ b/libc/arch-arm64/syscalls/fstat64.S @@ -13,5 +13,4 @@ ENTRY(fstat64) ret END(fstat64) - .globl fstat - .equ fstat, fstat64 +ALIAS_SYMBOL(fstat, fstat64) diff --git a/libc/arch-arm64/syscalls/fstatat64.S b/libc/arch-arm64/syscalls/fstatat64.S index dafd9823d..9f8f2c5bb 100644 --- a/libc/arch-arm64/syscalls/fstatat64.S +++ b/libc/arch-arm64/syscalls/fstatat64.S @@ -13,5 +13,4 @@ ENTRY(fstatat64) ret END(fstatat64) - .globl fstatat - .equ fstatat, fstatat64 +ALIAS_SYMBOL(fstatat, fstatat64) diff --git a/libc/arch-arm64/syscalls/ftruncate.S b/libc/arch-arm64/syscalls/ftruncate.S index c6e99f591..c21e098d7 100644 --- a/libc/arch-arm64/syscalls/ftruncate.S +++ b/libc/arch-arm64/syscalls/ftruncate.S @@ -13,5 +13,4 @@ ENTRY(ftruncate) ret END(ftruncate) - .globl ftruncate64 - .equ ftruncate64, ftruncate +ALIAS_SYMBOL(ftruncate64, ftruncate) diff --git a/libc/arch-arm64/syscalls/getrlimit.S b/libc/arch-arm64/syscalls/getrlimit.S index 518ab73d7..03ee9a8e8 100644 --- a/libc/arch-arm64/syscalls/getrlimit.S +++ b/libc/arch-arm64/syscalls/getrlimit.S @@ -13,5 +13,4 @@ ENTRY(getrlimit) ret END(getrlimit) - .globl getrlimit64 - .equ getrlimit64, getrlimit +ALIAS_SYMBOL(getrlimit64, getrlimit) diff --git a/libc/arch-arm64/syscalls/lseek.S b/libc/arch-arm64/syscalls/lseek.S index de96df042..93afeb750 100644 --- a/libc/arch-arm64/syscalls/lseek.S +++ b/libc/arch-arm64/syscalls/lseek.S @@ -13,5 +13,4 @@ ENTRY(lseek) ret END(lseek) - .globl lseek64 - .equ lseek64, lseek +ALIAS_SYMBOL(lseek64, lseek) diff --git a/libc/arch-arm64/syscalls/mmap.S b/libc/arch-arm64/syscalls/mmap.S index 64b955e26..65371bc58 100644 --- a/libc/arch-arm64/syscalls/mmap.S +++ b/libc/arch-arm64/syscalls/mmap.S @@ -13,5 +13,4 @@ ENTRY(mmap) ret END(mmap) - .globl mmap64 - .equ mmap64, mmap +ALIAS_SYMBOL(mmap64, mmap) diff --git a/libc/arch-arm64/syscalls/pread64.S b/libc/arch-arm64/syscalls/pread64.S index eafc04426..6c9f0e91a 100644 --- a/libc/arch-arm64/syscalls/pread64.S +++ b/libc/arch-arm64/syscalls/pread64.S @@ -13,5 +13,4 @@ ENTRY(pread64) ret END(pread64) - .globl pread - .equ pread, pread64 +ALIAS_SYMBOL(pread, pread64) diff --git a/libc/arch-arm64/syscalls/prlimit64.S b/libc/arch-arm64/syscalls/prlimit64.S index 2bece996f..9c018baca 100644 --- a/libc/arch-arm64/syscalls/prlimit64.S +++ b/libc/arch-arm64/syscalls/prlimit64.S @@ -13,5 +13,4 @@ ENTRY(prlimit64) ret END(prlimit64) - .globl prlimit - .equ prlimit, prlimit64 +ALIAS_SYMBOL(prlimit, prlimit64) diff --git a/libc/arch-arm64/syscalls/pwrite64.S b/libc/arch-arm64/syscalls/pwrite64.S index 6970954b6..1599c14df 100644 --- a/libc/arch-arm64/syscalls/pwrite64.S +++ b/libc/arch-arm64/syscalls/pwrite64.S @@ -13,5 +13,4 @@ ENTRY(pwrite64) ret END(pwrite64) - .globl pwrite - .equ pwrite, pwrite64 +ALIAS_SYMBOL(pwrite, pwrite64) diff --git a/libc/arch-arm64/syscalls/sendfile.S b/libc/arch-arm64/syscalls/sendfile.S index 17a0d4657..50ac12dbc 100644 --- a/libc/arch-arm64/syscalls/sendfile.S +++ b/libc/arch-arm64/syscalls/sendfile.S @@ -13,5 +13,4 @@ ENTRY(sendfile) ret END(sendfile) - .globl sendfile64 - .equ sendfile64, sendfile +ALIAS_SYMBOL(sendfile64, sendfile) diff --git a/libc/arch-arm64/syscalls/setrlimit.S b/libc/arch-arm64/syscalls/setrlimit.S index 6cb6b9898..52c75a1b9 100644 --- a/libc/arch-arm64/syscalls/setrlimit.S +++ b/libc/arch-arm64/syscalls/setrlimit.S @@ -13,5 +13,4 @@ ENTRY(setrlimit) ret END(setrlimit) - .globl setrlimit64 - .equ setrlimit64, setrlimit +ALIAS_SYMBOL(setrlimit64, setrlimit) diff --git a/libc/arch-arm64/syscalls/truncate.S b/libc/arch-arm64/syscalls/truncate.S index 0e5a33ec5..e01cc7d2e 100644 --- a/libc/arch-arm64/syscalls/truncate.S +++ b/libc/arch-arm64/syscalls/truncate.S @@ -13,5 +13,4 @@ ENTRY(truncate) ret END(truncate) - .globl truncate64 - .equ truncate64, truncate +ALIAS_SYMBOL(truncate64, truncate) diff --git a/libc/arch-mips/syscalls/_exit.S b/libc/arch-mips/syscalls/_exit.S index 5ac13249f..6e97aac5d 100644 --- a/libc/arch-mips/syscalls/_exit.S +++ b/libc/arch-mips/syscalls/_exit.S @@ -18,5 +18,4 @@ ENTRY(_exit) .set reorder END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-mips/syscalls/fstat64.S b/libc/arch-mips/syscalls/fstat64.S index 525c23c52..16e27914d 100644 --- a/libc/arch-mips/syscalls/fstat64.S +++ b/libc/arch-mips/syscalls/fstat64.S @@ -18,5 +18,4 @@ ENTRY(fstat64) .set reorder END(fstat64) - .globl fstat - .equ fstat, fstat64 +ALIAS_SYMBOL(fstat, fstat64) diff --git a/libc/arch-mips/syscalls/fstatat64.S b/libc/arch-mips/syscalls/fstatat64.S index f7b8e1d88..ebcf6b0c9 100644 --- a/libc/arch-mips/syscalls/fstatat64.S +++ b/libc/arch-mips/syscalls/fstatat64.S @@ -18,5 +18,4 @@ ENTRY(fstatat64) .set reorder END(fstatat64) - .globl fstatat - .equ fstatat, fstatat64 +ALIAS_SYMBOL(fstatat, fstatat64) diff --git a/libc/arch-mips64/syscalls/_exit.S b/libc/arch-mips64/syscalls/_exit.S index da5a2f7d1..37f811966 100644 --- a/libc/arch-mips64/syscalls/_exit.S +++ b/libc/arch-mips64/syscalls/_exit.S @@ -24,5 +24,4 @@ ENTRY(_exit) .set pop END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-mips64/syscalls/fallocate.S b/libc/arch-mips64/syscalls/fallocate.S index c1ef0ed93..14e25a091 100644 --- a/libc/arch-mips64/syscalls/fallocate.S +++ b/libc/arch-mips64/syscalls/fallocate.S @@ -24,5 +24,4 @@ ENTRY(fallocate) .set pop END(fallocate) - .globl fallocate64 - .equ fallocate64, fallocate +ALIAS_SYMBOL(fallocate64, fallocate) diff --git a/libc/arch-mips64/syscalls/ftruncate.S b/libc/arch-mips64/syscalls/ftruncate.S index 58b847b77..063e8f35c 100644 --- a/libc/arch-mips64/syscalls/ftruncate.S +++ b/libc/arch-mips64/syscalls/ftruncate.S @@ -24,5 +24,4 @@ ENTRY(ftruncate) .set pop END(ftruncate) - .globl ftruncate64 - .equ ftruncate64, ftruncate +ALIAS_SYMBOL(ftruncate64, ftruncate) diff --git a/libc/arch-mips64/syscalls/getrlimit.S b/libc/arch-mips64/syscalls/getrlimit.S index 7576c1710..5e2a82a46 100644 --- a/libc/arch-mips64/syscalls/getrlimit.S +++ b/libc/arch-mips64/syscalls/getrlimit.S @@ -24,5 +24,4 @@ ENTRY(getrlimit) .set pop END(getrlimit) - .globl getrlimit64 - .equ getrlimit64, getrlimit +ALIAS_SYMBOL(getrlimit64, getrlimit) diff --git a/libc/arch-mips64/syscalls/lseek.S b/libc/arch-mips64/syscalls/lseek.S index 5c92d7094..3bfc29dab 100644 --- a/libc/arch-mips64/syscalls/lseek.S +++ b/libc/arch-mips64/syscalls/lseek.S @@ -24,5 +24,4 @@ ENTRY(lseek) .set pop END(lseek) - .globl lseek64 - .equ lseek64, lseek +ALIAS_SYMBOL(lseek64, lseek) diff --git a/libc/arch-mips64/syscalls/mmap.S b/libc/arch-mips64/syscalls/mmap.S index 393271ae9..cc53eb26d 100644 --- a/libc/arch-mips64/syscalls/mmap.S +++ b/libc/arch-mips64/syscalls/mmap.S @@ -24,5 +24,4 @@ ENTRY(mmap) .set pop END(mmap) - .globl mmap64 - .equ mmap64, mmap +ALIAS_SYMBOL(mmap64, mmap) diff --git a/libc/arch-mips64/syscalls/pread64.S b/libc/arch-mips64/syscalls/pread64.S index 90e061237..7965ba933 100644 --- a/libc/arch-mips64/syscalls/pread64.S +++ b/libc/arch-mips64/syscalls/pread64.S @@ -24,5 +24,4 @@ ENTRY(pread64) .set pop END(pread64) - .globl pread - .equ pread, pread64 +ALIAS_SYMBOL(pread, pread64) diff --git a/libc/arch-mips64/syscalls/prlimit64.S b/libc/arch-mips64/syscalls/prlimit64.S index 5f0ba1de8..e04a5b631 100644 --- a/libc/arch-mips64/syscalls/prlimit64.S +++ b/libc/arch-mips64/syscalls/prlimit64.S @@ -24,5 +24,4 @@ ENTRY(prlimit64) .set pop END(prlimit64) - .globl prlimit - .equ prlimit, prlimit64 +ALIAS_SYMBOL(prlimit, prlimit64) diff --git a/libc/arch-mips64/syscalls/pwrite64.S b/libc/arch-mips64/syscalls/pwrite64.S index e34f8dba1..97e01831a 100644 --- a/libc/arch-mips64/syscalls/pwrite64.S +++ b/libc/arch-mips64/syscalls/pwrite64.S @@ -24,5 +24,4 @@ ENTRY(pwrite64) .set pop END(pwrite64) - .globl pwrite - .equ pwrite, pwrite64 +ALIAS_SYMBOL(pwrite, pwrite64) diff --git a/libc/arch-mips64/syscalls/sendfile.S b/libc/arch-mips64/syscalls/sendfile.S index f33024241..a50459e28 100644 --- a/libc/arch-mips64/syscalls/sendfile.S +++ b/libc/arch-mips64/syscalls/sendfile.S @@ -24,5 +24,4 @@ ENTRY(sendfile) .set pop END(sendfile) - .globl sendfile64 - .equ sendfile64, sendfile +ALIAS_SYMBOL(sendfile64, sendfile) diff --git a/libc/arch-mips64/syscalls/setrlimit.S b/libc/arch-mips64/syscalls/setrlimit.S index 0e5e80e4d..be6fdc3c8 100644 --- a/libc/arch-mips64/syscalls/setrlimit.S +++ b/libc/arch-mips64/syscalls/setrlimit.S @@ -24,5 +24,4 @@ ENTRY(setrlimit) .set pop END(setrlimit) - .globl setrlimit64 - .equ setrlimit64, setrlimit +ALIAS_SYMBOL(setrlimit64, setrlimit) diff --git a/libc/arch-mips64/syscalls/truncate.S b/libc/arch-mips64/syscalls/truncate.S index fb3b7eb5a..b8327965f 100644 --- a/libc/arch-mips64/syscalls/truncate.S +++ b/libc/arch-mips64/syscalls/truncate.S @@ -24,5 +24,4 @@ ENTRY(truncate) .set pop END(truncate) - .globl truncate64 - .equ truncate64, truncate +ALIAS_SYMBOL(truncate64, truncate) diff --git a/libc/arch-x86/syscalls/_exit.S b/libc/arch-x86/syscalls/_exit.S index 8528ee415..9945b35db 100644 --- a/libc/arch-x86/syscalls/_exit.S +++ b/libc/arch-x86/syscalls/_exit.S @@ -20,5 +20,4 @@ ENTRY(_exit) ret END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-x86/syscalls/fstat64.S b/libc/arch-x86/syscalls/fstat64.S index fc1623320..ba385a4f9 100644 --- a/libc/arch-x86/syscalls/fstat64.S +++ b/libc/arch-x86/syscalls/fstat64.S @@ -25,5 +25,4 @@ ENTRY(fstat64) ret END(fstat64) - .globl fstat - .equ fstat, fstat64 +ALIAS_SYMBOL(fstat, fstat64) diff --git a/libc/arch-x86/syscalls/fstatat64.S b/libc/arch-x86/syscalls/fstatat64.S index a3697e623..90e87b6ef 100644 --- a/libc/arch-x86/syscalls/fstatat64.S +++ b/libc/arch-x86/syscalls/fstatat64.S @@ -35,5 +35,4 @@ ENTRY(fstatat64) ret END(fstatat64) - .globl fstatat - .equ fstatat, fstatat64 +ALIAS_SYMBOL(fstatat, fstatat64) diff --git a/libc/arch-x86_64/syscalls/_exit.S b/libc/arch-x86_64/syscalls/_exit.S index c79091dd7..1ab4d4f6d 100644 --- a/libc/arch-x86_64/syscalls/_exit.S +++ b/libc/arch-x86_64/syscalls/_exit.S @@ -14,5 +14,4 @@ ENTRY(_exit) ret END(_exit) - .globl _Exit - .equ _Exit, _exit +ALIAS_SYMBOL(_Exit, _exit) diff --git a/libc/arch-x86_64/syscalls/fallocate.S b/libc/arch-x86_64/syscalls/fallocate.S index 8307f7e7a..f6f891b29 100644 --- a/libc/arch-x86_64/syscalls/fallocate.S +++ b/libc/arch-x86_64/syscalls/fallocate.S @@ -15,5 +15,4 @@ ENTRY(fallocate) ret END(fallocate) - .globl fallocate64 - .equ fallocate64, fallocate +ALIAS_SYMBOL(fallocate64, fallocate) diff --git a/libc/arch-x86_64/syscalls/fstat64.S b/libc/arch-x86_64/syscalls/fstat64.S index de576682c..a0d4fa1c1 100644 --- a/libc/arch-x86_64/syscalls/fstat64.S +++ b/libc/arch-x86_64/syscalls/fstat64.S @@ -14,5 +14,4 @@ ENTRY(fstat64) ret END(fstat64) - .globl fstat - .equ fstat, fstat64 +ALIAS_SYMBOL(fstat, fstat64) diff --git a/libc/arch-x86_64/syscalls/fstatat64.S b/libc/arch-x86_64/syscalls/fstatat64.S index 47785bb23..1984d68d1 100644 --- a/libc/arch-x86_64/syscalls/fstatat64.S +++ b/libc/arch-x86_64/syscalls/fstatat64.S @@ -15,5 +15,4 @@ ENTRY(fstatat64) ret END(fstatat64) - .globl fstatat - .equ fstatat, fstatat64 +ALIAS_SYMBOL(fstatat, fstatat64) diff --git a/libc/arch-x86_64/syscalls/ftruncate.S b/libc/arch-x86_64/syscalls/ftruncate.S index 0365368d1..791746849 100644 --- a/libc/arch-x86_64/syscalls/ftruncate.S +++ b/libc/arch-x86_64/syscalls/ftruncate.S @@ -14,5 +14,4 @@ ENTRY(ftruncate) ret END(ftruncate) - .globl ftruncate64 - .equ ftruncate64, ftruncate +ALIAS_SYMBOL(ftruncate64, ftruncate) diff --git a/libc/arch-x86_64/syscalls/getrlimit.S b/libc/arch-x86_64/syscalls/getrlimit.S index 2d272a125..00ed08a20 100644 --- a/libc/arch-x86_64/syscalls/getrlimit.S +++ b/libc/arch-x86_64/syscalls/getrlimit.S @@ -14,5 +14,4 @@ ENTRY(getrlimit) ret END(getrlimit) - .globl getrlimit64 - .equ getrlimit64, getrlimit +ALIAS_SYMBOL(getrlimit64, getrlimit) diff --git a/libc/arch-x86_64/syscalls/lseek.S b/libc/arch-x86_64/syscalls/lseek.S index 153b9352f..69d60c21e 100644 --- a/libc/arch-x86_64/syscalls/lseek.S +++ b/libc/arch-x86_64/syscalls/lseek.S @@ -14,5 +14,4 @@ ENTRY(lseek) ret END(lseek) - .globl lseek64 - .equ lseek64, lseek +ALIAS_SYMBOL(lseek64, lseek) diff --git a/libc/arch-x86_64/syscalls/mmap.S b/libc/arch-x86_64/syscalls/mmap.S index 8aa4780f6..0c25473c4 100644 --- a/libc/arch-x86_64/syscalls/mmap.S +++ b/libc/arch-x86_64/syscalls/mmap.S @@ -15,5 +15,4 @@ ENTRY(mmap) ret END(mmap) - .globl mmap64 - .equ mmap64, mmap +ALIAS_SYMBOL(mmap64, mmap) diff --git a/libc/arch-x86_64/syscalls/pread64.S b/libc/arch-x86_64/syscalls/pread64.S index 3aa56e595..eaa47b1b5 100644 --- a/libc/arch-x86_64/syscalls/pread64.S +++ b/libc/arch-x86_64/syscalls/pread64.S @@ -15,5 +15,4 @@ ENTRY(pread64) ret END(pread64) - .globl pread - .equ pread, pread64 +ALIAS_SYMBOL(pread, pread64) diff --git a/libc/arch-x86_64/syscalls/prlimit64.S b/libc/arch-x86_64/syscalls/prlimit64.S index 63ec492dd..737b863ab 100644 --- a/libc/arch-x86_64/syscalls/prlimit64.S +++ b/libc/arch-x86_64/syscalls/prlimit64.S @@ -15,5 +15,4 @@ ENTRY(prlimit64) ret END(prlimit64) - .globl prlimit - .equ prlimit, prlimit64 +ALIAS_SYMBOL(prlimit, prlimit64) diff --git a/libc/arch-x86_64/syscalls/pwrite64.S b/libc/arch-x86_64/syscalls/pwrite64.S index 2779fb419..edb60af49 100644 --- a/libc/arch-x86_64/syscalls/pwrite64.S +++ b/libc/arch-x86_64/syscalls/pwrite64.S @@ -15,5 +15,4 @@ ENTRY(pwrite64) ret END(pwrite64) - .globl pwrite - .equ pwrite, pwrite64 +ALIAS_SYMBOL(pwrite, pwrite64) diff --git a/libc/arch-x86_64/syscalls/sendfile.S b/libc/arch-x86_64/syscalls/sendfile.S index 117b0aad6..c0fa4ee26 100644 --- a/libc/arch-x86_64/syscalls/sendfile.S +++ b/libc/arch-x86_64/syscalls/sendfile.S @@ -15,5 +15,4 @@ ENTRY(sendfile) ret END(sendfile) - .globl sendfile64 - .equ sendfile64, sendfile +ALIAS_SYMBOL(sendfile64, sendfile) diff --git a/libc/arch-x86_64/syscalls/setrlimit.S b/libc/arch-x86_64/syscalls/setrlimit.S index ef0306829..3843ff9ae 100644 --- a/libc/arch-x86_64/syscalls/setrlimit.S +++ b/libc/arch-x86_64/syscalls/setrlimit.S @@ -14,5 +14,4 @@ ENTRY(setrlimit) ret END(setrlimit) - .globl setrlimit64 - .equ setrlimit64, setrlimit +ALIAS_SYMBOL(setrlimit64, setrlimit) diff --git a/libc/arch-x86_64/syscalls/truncate.S b/libc/arch-x86_64/syscalls/truncate.S index 2ecd05b6e..4b953a38b 100644 --- a/libc/arch-x86_64/syscalls/truncate.S +++ b/libc/arch-x86_64/syscalls/truncate.S @@ -14,5 +14,4 @@ ENTRY(truncate) ret END(truncate) - .globl truncate64 - .equ truncate64, truncate +ALIAS_SYMBOL(truncate64, truncate) diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py index 7e11418db..4d0afe20f 100755 --- a/libc/tools/gensyscalls.py +++ b/libc/tools/gensyscalls.py @@ -56,12 +56,6 @@ ENTRY(%(func)s) """ -function_alias = """ - .globl %(alias)s - .equ %(alias)s, %(func)s -""" - - # # ARM assembler templates for each syscall stub # @@ -284,7 +278,7 @@ def add_footer(pointer_length, stub, syscall): # Add any aliases for this syscall. aliases = syscall["aliases"] for alias in aliases: - stub += function_alias % { "func" : syscall["func"], "alias" : alias } + stub += "\nALIAS_SYMBOL(%s, %s)\n" % (alias, syscall["func"]) # Use hidden visibility on LP64 for any functions beginning with underscores. # Force hidden visibility for any functions which begin with 3 underscores From 1f95ffecc22995abe6ac8cdd6dbb74f6f9a42b2f Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 2 Mar 2015 17:43:42 +0000 Subject: [PATCH 024/123] Changes to re-enable overrides for tz data Bug: 10637303 Change-Id: I5d525b66cf30d34b421803b876445596bed8d64d --- libc/tzcode/localtime.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c index 29f605c08..bf09c5e0e 100644 --- a/libc/tzcode/localtime.c +++ b/libc/tzcode/localtime.c @@ -2253,11 +2253,14 @@ static int __bionic_open_tzdata_path(const char* path_prefix_variable, const cha } static int __bionic_open_tzdata(const char* olson_id, int* data_size) { - int fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id, data_size); - if (fd == -2) { - // The first thing that 'recovery' does is try to format the current time. It doesn't have - // any tzdata available, so we must not abort here --- doing so breaks the recovery image! - fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id); + int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata", olson_id, data_size); + if (fd < 0) { + fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id, data_size); + if (fd == -2) { + // The first thing that 'recovery' does is try to format the current time. It doesn't have + // any tzdata available, so we must not abort here --- doing so breaks the recovery image! + fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id); + } } return fd; } From 24958514b92c9b9e111223e4e4c56ef1a52b6403 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 25 Mar 2015 09:12:00 -0700 Subject: [PATCH 025/123] Use ALIAS_SYMBOL for assembler aliasing. Change-Id: I8d04d2da0a1ac440cc9044fc819c9a8eda5ff17d --- libc/arch-arm/bionic/setjmp.S | 6 ++---- libc/arch-arm64/bionic/setjmp.S | 6 ++---- libc/arch-mips/bionic/setjmp.S | 11 ++--------- libc/arch-x86/bionic/setjmp.S | 6 ++---- libc/arch-x86_64/bionic/setjmp.S | 6 ++---- 5 files changed, 10 insertions(+), 25 deletions(-) diff --git a/libc/arch-arm/bionic/setjmp.S b/libc/arch-arm/bionic/setjmp.S index 8d7786c12..8220c0825 100644 --- a/libc/arch-arm/bionic/setjmp.S +++ b/libc/arch-arm/bionic/setjmp.S @@ -169,7 +169,5 @@ ENTRY(siglongjmp) bx lr END(siglongjmp) - .globl longjmp - .equ longjmp, siglongjmp - .globl _longjmp - .equ _longjmp, siglongjmp +ALIAS_SYMBOL(longjmp, siglongjmp) +ALIAS_SYMBOL(_longjmp, siglongjmp) diff --git a/libc/arch-arm64/bionic/setjmp.S b/libc/arch-arm64/bionic/setjmp.S index 6e119dc84..ba0a226e0 100644 --- a/libc/arch-arm64/bionic/setjmp.S +++ b/libc/arch-arm64/bionic/setjmp.S @@ -146,7 +146,5 @@ ENTRY(siglongjmp) ret END(siglongjmp) - .globl longjmp - .equ longjmp, siglongjmp - .globl _longjmp - .equ _longjmp, siglongjmp +ALIAS_SYMBOL(longjmp, siglongjmp) +ALIAS_SYMBOL(_longjmp, siglongjmp) diff --git a/libc/arch-mips/bionic/setjmp.S b/libc/arch-mips/bionic/setjmp.S index 1c2655385..bed956257 100644 --- a/libc/arch-mips/bionic/setjmp.S +++ b/libc/arch-mips/bionic/setjmp.S @@ -352,12 +352,5 @@ longjmp_botch: jal abort END(siglongjmp) - - .globl longjmp - .type longjmp, @function - .equ longjmp, siglongjmp # alias for siglongjmp - - - .globl _longjmp - .type _longjmp, @function - .equ _longjmp, siglongjmp # alias for siglongjmp +ALIAS_SYMBOL(longjmp, siglongjmp) +ALIAS_SYMBOL(_longjmp, siglongjmp) diff --git a/libc/arch-x86/bionic/setjmp.S b/libc/arch-x86/bionic/setjmp.S index 25a016d1b..18ad810ee 100644 --- a/libc/arch-x86/bionic/setjmp.S +++ b/libc/arch-x86/bionic/setjmp.S @@ -123,7 +123,5 @@ ENTRY(siglongjmp) ret END(siglongjmp) - .globl longjmp - .equ longjmp, siglongjmp - .globl _longjmp - .equ _longjmp, siglongjmp +ALIAS_SYMBOL(longjmp, siglongjmp) +ALIAS_SYMBOL(_longjmp, siglongjmp) diff --git a/libc/arch-x86_64/bionic/setjmp.S b/libc/arch-x86_64/bionic/setjmp.S index 28981fad0..5559f54c3 100644 --- a/libc/arch-x86_64/bionic/setjmp.S +++ b/libc/arch-x86_64/bionic/setjmp.S @@ -129,7 +129,5 @@ ENTRY(siglongjmp) ret END(siglongjmp) - .globl longjmp - .equ longjmp, siglongjmp - .globl _longjmp - .equ _longjmp, siglongjmp +ALIAS_SYMBOL(longjmp, siglongjmp) +ALIAS_SYMBOL(_longjmp, siglongjmp) From d2bd5739ed005ddcfd9d91d42dc6d97f7db9ecbf Mon Sep 17 00:00:00 2001 From: Simon Baldwin Date: Wed, 25 Mar 2015 16:14:30 +0000 Subject: [PATCH 026/123] Remove unused/unusable relocation packer files. Removes: - src/run_length_encoder.h artifact of the original packing tool, not used here - test_data/generate_elf_file_unittest_relocs.sh - test_data/generate_elf_file_unittest_relocs.py test data generation for chromium/gyp, not usable here - README.TXT because it is now almost entirely outdated Change-Id: Ic4cd372647d9a365dc52833a6cc1cf66f0c95ec9 --- tools/relocation_packer/README.TXT | 135 ------------------ .../src/run_length_encoder.h | 81 ----------- .../generate_elf_file_unittest_relocs.py | 88 ------------ .../generate_elf_file_unittest_relocs.sh | 35 ----- 4 files changed, 339 deletions(-) delete mode 100644 tools/relocation_packer/README.TXT delete mode 100644 tools/relocation_packer/src/run_length_encoder.h delete mode 100755 tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py delete mode 100755 tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh diff --git a/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT deleted file mode 100644 index 071ab5df2..000000000 --- a/tools/relocation_packer/README.TXT +++ /dev/null @@ -1,135 +0,0 @@ -Introduction: -------------- - -Relative relocations are the bulk of dynamic relocations (the .rel.dyn -or .rela.dyn sections) in libchrome..so. The ELF standard -representation of them is wasteful. - -Packing uses a combination of run length encoding, delta encoding, and LEB128 -encoding to store them more efficiently. Packed relocations are placed in -a new .android.rel.dyn or .android.rela.dyn section. Packing reduces -the footprint of libchrome..so in the filesystem, in APK downloads, -and in memory when loaded on the device. - -A packed libchrome..so is designed so that it can be loaded directly -on Android, but requires the explicit support of a crazy linker that has been -extended to understand packed relocations. Packed relocations are currently -only supported on ARM. - -A packed libchrome..so cannot currently be used with the standard -Android runtime linker. - -See src/*.h for design and implementation notes. - - -Notes: ------- - -Packing does not adjust debug data. An unstripped libchrome..so -can be packed and will run, but may no longer be useful for debugging. - -Unpacking on the device requires the explicit support of an extended crazy -linker. Adds the following new .dynamic tags, used by the crazy linker to -find the packed .android.rel.dyn or .android.rela.dyn section data: - - DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d) - - The offset of packed relocation data in libchrome..so - DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e) - - The size of packed relocation data in bytes - -32 bit ARM libraries use relocations without addends. 64 bit ARM libraries -use relocations with addends. The packing strategy necessarily differs for -the two relocation types. - -Where libchrome..so contains relocations without addends, the format -of .android.rel.dyn data is: - - "APR1" identifier - N: the number of count-delta pairs in the encoding - A: the initial offset - N * C,D: N count-delta pairs - -Where libchrome..so contains relocations with addends, the format -of .android.rela.dyn data is: - - "APA1" identifier - N: the number of addr-addend delta pairs in the encoding - N * A,V: N addr-addend delta pairs - -All numbers in the encoding stream are stored as LEB128 values. For details -see http://en.wikipedia.org/wiki/LEB128. - -The streaming unpacking algorithm for 32 bit ARM is: - - skip over "APR1" - pairs, addr = next leb128 value, next leb128 value - emit R_ARM_RELATIVE relocation with r_offset = addr - while pairs: - count, delta = next leb128 value, next leb128 value - while count: - addr += delta - emit R_ARM_RELATIVE relocation with r_offset = addr - count-- - pairs-- - -The streaming unpacking algorithm for 64 bit ARM is: - - skip over "APA1" - pairs = next signed leb128 value - addr, addend = 0, 0 - while pairs: - addr += next signed leb128 value - addend += next signed leb128 value - emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend - pairs-- - - -Usage instructions: -------------------- - -To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and -then run the tool: - - echo -n 'NULL' >/tmp/small - if file libchrome..so | grep -q 'ELF 32'; then - arm-linux-androideabi-objcopy - --add-section .android.rel.dyn=/tmp/small - libchrome..so libchrome..so.packed - else - aarch64-linux-android-objcopy - --add-section .android.rela.dyn=/tmp/small - libchrome..so libchrome..so.packed - fi - rm /tmp/small - relocation_packer libchrome..so.packed - -To unpack and restore the shared library to its original state: - - cp libchrome..so.packed unpackable - relocation_packer -u unpackable - if file libchrome..so | grep -q 'ELF 32'; then - arm-linux-androideabi-objcopy \ - --remove-section=.android.rel.dyn unpackable libchrome..so - else - aarch64-linux-android-objcopy \ - --remove-section=.android.rela.dyn unpackable libchrome..so - endif - rm unpackable - - -Bugs & TODOs: -------------- - -Requires two free slots in the .dynamic section. Uses these to add data that -tells the crazy linker where to find the packed relocation data. Fails -if insufficient free slots exist (use gold --spare-dynamic-slots to increase -the allocation). - -Requires libelf 0.158 or later. Earlier libelf releases may be buggy in -ways that prevent the packer from working correctly. - - -Testing: --------- - -Unittests run under gtest, on the host system. diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h deleted file mode 100644 index f3a80e602..000000000 --- a/tools/relocation_packer/src/run_length_encoder.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Run-length encode and decode relative relocations. -// -// Relative relocations are the bulk of dynamic relocations (the -// .rel.dyn or .rela.dyn sections) in libchrome..so, and the ELF -// standard representation of them is wasteful. .rel.dyn contains -// relocations without addends, .rela.dyn relocations with addends. -// -// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes -// on 64 bit plaforms, split into offset and info fields. Offsets strictly -// increase, and each is commonly a few bytes different from its predecessor. -// There are long runs where the difference does not change. The info field -// is constant. Example, from 'readelf -x4 libchrome..so' 32 bit: -// -// offset info offset info -// 808fef01 17000000 848fef01 17000000 ................ -// 888fef01 17000000 8c8fef01 17000000 ................ -// 908fef01 17000000 948fef01 17000000 ................ -// -// Run length encoding packs this data more efficiently, by representing it -// as a delta and a count of entries each differing from its predecessor -// by this delta. The above can be represented as a start address followed -// by an encoded count of 6 and offset difference of 4: -// -// start count diff -// 01ef8f80 00000006 00000004 -// -// Because relative relocation offsets strictly increase, the complete -// set of relative relocations in libchrome..so can be -// represented by a single start address followed by one or more difference -// and count encoded word pairs: -// -// start run1 count run1 diff run2 count run2 diff -// 01ef8f80 00000006 00000004 00000010 00000008 ... -// -// Decoding regenerates relative relocations beginning at address -// 'start' and for each encoded run, incrementing the address by 'difference' -// for 'count' iterations and emitting a new relative relocation. -// -// Once encoded, data is prefixed by a single word count of packed delta and -// count pairs. A final run-length encoded relative relocations vector -// might therefore look something like: -// -// pairs start run 1 run 2 ... run 15 -// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ... -// Interpreted as: -// pairs=15 start=.. count=6,delta=4 count=16,delta=8 - -#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ -#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ - -#include - -#include "elf.h" -#include "elf_traits.h" - -namespace relocation_packer { - -// A RelocationRunLengthCodec packs vectors of relative relocations -// into more compact forms, and unpacks them to reproduce the pre-packed data. -class RelocationRunLengthCodec { - public: - // Encode relative relocations into a more compact form. - // |relocations| is a vector of relative relocation structs. - // |packed| is the vector of packed words into which relocations are packed. - static void Encode(const std::vector& relocations, - std::vector* packed); - - // Decode relative relocations from their more compact form. - // |packed| is the vector of packed relocations. - // |relocations| is a vector of unpacked relative relocation structs. - static void Decode(const std::vector& packed, - std::vector* relocations); -}; - -} // namespace relocation_packer - -#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py deleted file mode 100755 index e71b5cbbd..000000000 --- a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Build relocation packer unit test data. - -Uses a built relocation packer to generate 'golden' reference test data -files for elf_file_unittests.cc. -""" - -import optparse -import os -import shutil -import subprocess -import sys -import tempfile - -def PackArmLibraryRelocations(android_pack_relocations, - android_objcopy, - added_section, - input_path, - output_path): - # Copy and add a 'NULL' .android.rel.dyn section for the packing tool. - with tempfile.NamedTemporaryFile() as stream: - stream.write('NULL') - stream.flush() - objcopy_command = [android_objcopy, - '--add-section', '%s=%s' % (added_section, stream.name), - input_path, output_path] - subprocess.check_call(objcopy_command) - - # Pack relocations. - pack_command = [android_pack_relocations, output_path] - subprocess.check_call(pack_command) - - -def UnpackArmLibraryRelocations(android_pack_relocations, - input_path, - output_path): - shutil.copy(input_path, output_path) - - # Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn - # in place. - unpack_command = [android_pack_relocations, '-u', output_path] - subprocess.check_call(unpack_command) - - -def main(): - parser = optparse.OptionParser() - - parser.add_option('--android-pack-relocations', - help='Path to the ARM relocations packer binary') - parser.add_option('--android-objcopy', - help='Path to the toolchain\'s objcopy binary') - parser.add_option('--added-section', - choices=['.android.rel.dyn', '.android.rela.dyn'], - help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"') - parser.add_option('--test-file', - help='Path to the input test file, an unpacked ARM .so') - parser.add_option('--unpacked-output', - help='Path to the output file for reference unpacked data') - parser.add_option('--packed-output', - help='Path to the output file for reference packed data') - - options, _ = parser.parse_args() - - for output in [options.unpacked_output, options.packed_output]: - directory = os.path.dirname(output) - if not os.path.exists(directory): - os.makedirs(directory) - - PackArmLibraryRelocations(options.android_pack_relocations, - options.android_objcopy, - options.added_section, - options.test_file, - options.packed_output) - - UnpackArmLibraryRelocations(options.android_pack_relocations, - options.packed_output, - options.unpacked_output) - - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh deleted file mode 100755 index f90a2f658..000000000 --- a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files -# from elf_file_unittest_relocs.cc. Run once to create these test data -# files; the files are checked into the source tree. -# -# To use: -# ./generate_elf_file_unittest_relocs.sh -# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so - -function main() { - local '-r' test_data_directory="$(pwd)" - cd '../../..' - - source tools/cr/cr-bash-helpers.sh - local arch - for arch in 'arm32' 'arm64'; do - cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}" - cr 'build' 'relocation_packer_unittests_test_data' - done - - local '-r' packer='out_android/Debug/obj/tools/relocation_packer' - local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen" - - cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \ - "${test_data_directory}" - - return 0 -} - -main From 28e69f75088684b41d30b051799d7687d33f2205 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 25 Mar 2015 12:36:18 -0700 Subject: [PATCH 027/123] Add O_APPEND flag for __libc_write_stderr. For DeathTests, we are testing the output of stderr to check if it is the death we are expecting. To collect the output, Gtest redirects stderr to a temporary file. But in __libc_write_stderr in libc_logging.cpp, we are writing to stderr without a O_APPEND flag, so a new message will overwrite a previous message. The above situation makes almost all the DeathTests fail on host. Because the expected message are always overwritten in host DeathTests. So I add O_APPEND flag in __libc_write_stderr, which makes all host DeathTests pass. Change-Id: Ic2f6044fdb181eebe132a6f170b57db43c5c3289 --- libc/bionic/libc_logging.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp index 2eb9d68f8..7ad21c4cd 100644 --- a/libc/bionic/libc_logging.cpp +++ b/libc/bionic/libc_logging.cpp @@ -427,7 +427,7 @@ int __libc_format_fd(int fd, const char* format, ...) { } static int __libc_write_stderr(const char* tag, const char* msg) { - int fd = TEMP_FAILURE_RETRY(open("/dev/stderr", O_CLOEXEC | O_WRONLY)); + int fd = TEMP_FAILURE_RETRY(open("/dev/stderr", O_CLOEXEC | O_WRONLY | O_APPEND)); if (fd == -1) { return -1; } From 03324780aae9ff28c8acf52debf0ea39120e5ab8 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 24 Mar 2015 17:43:14 -0700 Subject: [PATCH 028/123] Cause Fatal error when invalid pthread_id is detected. This is a patch testing whether we can use abort() instead of returning ESRCH for invalid pthread ids. It is an intermediate step to remove g_thread_list/g_thread_list_lock. Bug: 19636317 Change-Id: Idd8e4a346c7ce91e1be0c2ebcb78ce51c0d0a31d --- libc/bionic/pthread_internal.cpp | 12 ++++++--- tests/pthread_test.cpp | 46 +++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp index 1967ccf8c..afafad84c 100644 --- a/libc/bionic/pthread_internal.cpp +++ b/libc/bionic/pthread_internal.cpp @@ -81,12 +81,16 @@ void __pthread_internal_remove_and_free(pthread_internal_t* thread) { pthread_internal_t* __pthread_internal_find(pthread_t thread_id) { pthread_internal_t* thread = reinterpret_cast(thread_id); - ScopedPthreadMutexLocker locker(&g_thread_list_lock); + { + ScopedPthreadMutexLocker locker(&g_thread_list_lock); - for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) { - if (t == thread) { - return thread; + for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) { + if (t == thread) { + return thread; + } } } + // TODO: Remove g_thread_list/g_thread_list_lock when the fatal error below doesn't happen. + __libc_fatal("No such thread: %p", thread); return NULL; } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 4eb352d8e..b4667f939 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -399,12 +399,17 @@ TEST(pthread, pthread_setname_np__other) { ASSERT_EQ(0, pthread_setname_np(t1, "short 2")); } -TEST(pthread, pthread_setname_np__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_setname_np__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); // Call pthread_setname_np after thread has already exited. +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_setname_np(dead_thread, "short 3"), testing::KilledBySignal(SIGABRT), + "No such thread"); +#else ASSERT_EQ(ENOENT, pthread_setname_np(dead_thread, "short 3")); +#endif } TEST(pthread, pthread_kill__0) { @@ -430,11 +435,15 @@ TEST(pthread, pthread_kill__in_signal_handler) { ASSERT_EQ(0, pthread_kill(pthread_self(), SIGALRM)); } -TEST(pthread, pthread_detach__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_detach__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_detach(dead_thread), testing::KilledBySignal(SIGABRT), "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_detach(dead_thread)); +#endif } TEST(pthread, pthread_detach_no_leak) { @@ -485,44 +494,67 @@ TEST(pthread, pthread_getcpuclockid__clock_gettime) { ASSERT_EQ(0, clock_gettime(c, &ts)); } -TEST(pthread, pthread_getcpuclockid__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_getcpuclockid__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); clockid_t c; +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_getcpuclockid(dead_thread, &c), testing::KilledBySignal(SIGABRT), + "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_getcpuclockid(dead_thread, &c)); +#endif } -TEST(pthread, pthread_getschedparam__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_getschedparam__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); int policy; sched_param param; +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_getschedparam(dead_thread, &policy, ¶m), testing::KilledBySignal(SIGABRT), + "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_getschedparam(dead_thread, &policy, ¶m)); +#endif } -TEST(pthread, pthread_setschedparam__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_setschedparam__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); int policy = 0; sched_param param; +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_setschedparam(dead_thread, policy, ¶m), testing::KilledBySignal(SIGABRT), + "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_setschedparam(dead_thread, policy, ¶m)); +#endif } -TEST(pthread, pthread_join__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_join__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_join(dead_thread, NULL), testing::KilledBySignal(SIGABRT), "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_join(dead_thread, NULL)); +#endif } -TEST(pthread, pthread_kill__no_such_thread) { +TEST_F(pthread_DeathTest, pthread_kill__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); +#if defined(__BIONIC__) + ASSERT_EXIT(pthread_kill(dead_thread, 0), testing::KilledBySignal(SIGABRT), "No such thread"); +#else ASSERT_EQ(ESRCH, pthread_kill(dead_thread, 0)); +#endif } TEST(pthread, pthread_join__multijoin) { From 799cb35f45a161de96b272de38724f77e988f5f3 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 25 Mar 2015 16:18:21 -0700 Subject: [PATCH 029/123] Fix in error handling in pthread_create.cpp. It is due to a previous change "Let g_thread_list_lock only protect g_thread_list". We need to add the newly created thread to thread_list even if __init_thread fails, so the thread can exit successfully. Change-Id: I0332df11acfdd181350bcc092b12d90d679057a4 --- libc/bionic/pthread_create.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 3d73d52e5..66632c494 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -267,6 +267,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, // Mark the thread detached and replace its start_routine with a no-op. // Letting the thread run is the easiest way to clean up its resources. atomic_store(&thread->join_state, THREAD_DETACHED); + __pthread_internal_add(thread); thread->start_routine = __do_nothing; pthread_mutex_unlock(&thread->startup_handshake_mutex); return init_errno; From 17393b06bab9cb3e95d0f466a56c746de19b8eee Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Sat, 21 Mar 2015 15:08:25 -0700 Subject: [PATCH 030/123] Hide internal of pthread_mutex_t. Bug: 19249079 Change-Id: Iffb79c8d861b698d474f212dc80c638fc2cf1620 --- libc/bionic/pthread_mutex.cpp | 229 +++++++++++++++++----------------- libc/include/pthread.h | 54 ++++---- libc/stdio/fileext.h | 6 +- tests/pthread_test.cpp | 132 ++++++++++++-------- 4 files changed, 226 insertions(+), 195 deletions(-) diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index 24066ae02..d2ff1aeb0 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,7 @@ #define MUTEX_STATE_FROM_BITS(v) FIELD_FROM_BITS(v, MUTEX_STATE_SHIFT, MUTEX_STATE_LEN) #define MUTEX_STATE_TO_BITS(v) FIELD_TO_BITS(v, MUTEX_STATE_SHIFT, MUTEX_STATE_LEN) -#define MUTEX_STATE_UNLOCKED 0 /* must be 0 to match __PTHREAD_MUTEX_INIT_VALUE */ +#define MUTEX_STATE_UNLOCKED 0 /* must be 0 to match PTHREAD_MUTEX_INITIALIZER */ #define MUTEX_STATE_LOCKED_UNCONTENDED 1 /* must be 1 due to atomic dec in unlock operation */ #define MUTEX_STATE_LOCKED_CONTENDED 2 /* must be 1 + LOCKED_UNCONTENDED due to atomic dec */ @@ -122,30 +123,17 @@ #define MUTEX_SHARED_MASK FIELD_MASK(MUTEX_SHARED_SHIFT,1) /* Mutex type: - * * We support normal, recursive and errorcheck mutexes. - * - * The constants defined here *cannot* be changed because they must match - * the C library ABI which defines the following initialization values in - * : - * - * __PTHREAD_MUTEX_INIT_VALUE - * __PTHREAD_RECURSIVE_MUTEX_VALUE - * __PTHREAD_ERRORCHECK_MUTEX_INIT_VALUE */ #define MUTEX_TYPE_SHIFT 14 #define MUTEX_TYPE_LEN 2 #define MUTEX_TYPE_MASK FIELD_MASK(MUTEX_TYPE_SHIFT,MUTEX_TYPE_LEN) -#define MUTEX_TYPE_NORMAL 0 /* Must be 0 to match __PTHREAD_MUTEX_INIT_VALUE */ -#define MUTEX_TYPE_RECURSIVE 1 -#define MUTEX_TYPE_ERRORCHECK 2 - #define MUTEX_TYPE_TO_BITS(t) FIELD_TO_BITS(t, MUTEX_TYPE_SHIFT, MUTEX_TYPE_LEN) -#define MUTEX_TYPE_BITS_NORMAL MUTEX_TYPE_TO_BITS(MUTEX_TYPE_NORMAL) -#define MUTEX_TYPE_BITS_RECURSIVE MUTEX_TYPE_TO_BITS(MUTEX_TYPE_RECURSIVE) -#define MUTEX_TYPE_BITS_ERRORCHECK MUTEX_TYPE_TO_BITS(MUTEX_TYPE_ERRORCHECK) +#define MUTEX_TYPE_BITS_NORMAL MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_NORMAL) +#define MUTEX_TYPE_BITS_RECURSIVE MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_RECURSIVE) +#define MUTEX_TYPE_BITS_ERRORCHECK MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_ERRORCHECK) /* Mutex owner field: * @@ -237,55 +225,66 @@ int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared) return 0; } -static inline atomic_int* get_mutex_value_pointer(pthread_mutex_t* mutex) { - static_assert(sizeof(atomic_int) == sizeof(mutex->value), - "mutex->value should actually be atomic_int in implementation."); +struct pthread_mutex_internal_t { + atomic_int state; +#if defined(__LP64__) + char __reserved[36]; +#endif +}; - // We prefer casting to atomic_int instead of declaring mutex->value to be atomic_int directly. - // Because using the second method pollutes pthread.h, and causes an error when compiling libcxx. - return reinterpret_cast(&mutex->value); +static_assert(sizeof(pthread_mutex_t) == sizeof(pthread_mutex_internal_t), + "pthread_mutex_t should actually be pthread_mutex_internal_t in implementation."); + +// For binary compatibility with old version of pthread_mutex_t, we can't use more strict alignment +// than 4-byte alignment. +static_assert(alignof(pthread_mutex_t) == 4, + "pthread_mutex_t should fulfill the alignment of pthread_mutex_internal_t."); + +static inline pthread_mutex_internal_t* __get_internal_mutex(pthread_mutex_t* mutex_interface) { + return reinterpret_cast(mutex_interface); } -int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) { - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); +int pthread_mutex_init(pthread_mutex_t* mutex_interface, const pthread_mutexattr_t* attr) { + pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); + + memset(mutex, 0, sizeof(pthread_mutex_internal_t)); if (__predict_true(attr == NULL)) { - atomic_init(mutex_value_ptr, MUTEX_TYPE_BITS_NORMAL); + atomic_init(&mutex->state, MUTEX_TYPE_BITS_NORMAL); return 0; } - int value = 0; + int state = 0; if ((*attr & MUTEXATTR_SHARED_MASK) != 0) { - value |= MUTEX_SHARED_MASK; + state |= MUTEX_SHARED_MASK; } switch (*attr & MUTEXATTR_TYPE_MASK) { case PTHREAD_MUTEX_NORMAL: - value |= MUTEX_TYPE_BITS_NORMAL; + state |= MUTEX_TYPE_BITS_NORMAL; break; case PTHREAD_MUTEX_RECURSIVE: - value |= MUTEX_TYPE_BITS_RECURSIVE; + state |= MUTEX_TYPE_BITS_RECURSIVE; break; case PTHREAD_MUTEX_ERRORCHECK: - value |= MUTEX_TYPE_BITS_ERRORCHECK; + state |= MUTEX_TYPE_BITS_ERRORCHECK; break; default: return EINVAL; } - atomic_init(mutex_value_ptr, value); + atomic_init(&mutex->state, state); return 0; } -static inline int __pthread_normal_mutex_trylock(atomic_int* mutex_value_ptr, int shared) { +static inline __always_inline int __pthread_normal_mutex_trylock(pthread_mutex_internal_t* mutex, + int shared) { const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - int mvalue = unlocked; - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, - locked_uncontended, - memory_order_acquire, - memory_order_relaxed))) { + int old_state = unlocked; + if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, + locked_uncontended, memory_order_acquire, memory_order_relaxed))) { return 0; } return EBUSY; @@ -303,9 +302,11 @@ static inline int __pthread_normal_mutex_trylock(atomic_int* mutex_value_ptr, in * "type" value is zero, so the only bits that will be set are the ones in * the lock state field. */ -static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int shared, - const timespec* abs_timeout_or_null, clockid_t clock) { - if (__predict_true(__pthread_normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { +static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex, + int shared, + const timespec* abs_timeout_or_null, + clockid_t clock) { + if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { return 0; } @@ -316,13 +317,13 @@ static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int s // We want to go to sleep until the mutex is available, which requires // promoting it to locked_contended. We need to swap in the new state - // value and then wait until somebody wakes us up. + // and then wait until somebody wakes us up. // An atomic_exchange is used to compete with other threads for the lock. // If it returns unlocked, we have acquired the lock, otherwise another // thread still holds the lock and we should wait again. // If lock is acquired, an acquire fence is needed to make all memory accesses // made by other threads visible to the current CPU. - while (atomic_exchange_explicit(mutex_value_ptr, locked_contended, + while (atomic_exchange_explicit(&mutex->state, locked_contended, memory_order_acquire) != unlocked) { timespec ts; timespec* rel_timeout = NULL; @@ -332,7 +333,7 @@ static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int s return ETIMEDOUT; } } - if (__futex_wait_ex(mutex_value_ptr, shared, locked_contended, rel_timeout) == -ETIMEDOUT) { + if (__futex_wait_ex(&mutex->state, shared, locked_contended, rel_timeout) == -ETIMEDOUT) { return ETIMEDOUT; } } @@ -343,7 +344,8 @@ static inline int __pthread_normal_mutex_lock(atomic_int* mutex_value_ptr, int s * Release a mutex of type NORMAL. The caller is responsible for determining * that we are in fact the owner of this lock. */ -static inline void __pthread_normal_mutex_unlock(atomic_int* mutex_value_ptr, int shared) { +static inline __always_inline void __pthread_normal_mutex_unlock(pthread_mutex_internal_t* mutex, + int shared) { const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; @@ -352,7 +354,7 @@ static inline void __pthread_normal_mutex_unlock(atomic_int* mutex_value_ptr, in // one of them. // A release fence is required to make previous stores visible to next // lock owner threads. - if (atomic_exchange_explicit(mutex_value_ptr, unlocked, + if (atomic_exchange_explicit(&mutex->state, unlocked, memory_order_release) == locked_contended) { // Wake up one waiting thread. We don't know which thread will be // woken or when it'll start executing -- futexes make no guarantees @@ -372,7 +374,7 @@ static inline void __pthread_normal_mutex_unlock(atomic_int* mutex_value_ptr, in // we call wake, the thread we eventually wake will find an unlocked mutex // and will execute. Either way we have correct behavior and nobody is // orphaned on the wait queue. - __futex_wake_ex(mutex_value_ptr, shared, 1); + __futex_wake_ex(&mutex->state, shared, 1); } } @@ -382,11 +384,12 @@ static inline void __pthread_normal_mutex_unlock(atomic_int* mutex_value_ptr, in * Otherwise, it atomically increments the counter and returns 0. * */ -static inline int __recursive_increment(atomic_int* mutex_value_ptr, int mvalue) { +static inline __always_inline int __recursive_increment(pthread_mutex_internal_t* mutex, + int old_state) { // Detect recursive lock overflow and return EAGAIN. // This is safe because only the owner thread can modify the // counter bits in the mutex value. - if (MUTEX_COUNTER_BITS_WILL_OVERFLOW(mvalue)) { + if (MUTEX_COUNTER_BITS_WILL_OVERFLOW(old_state)) { return EAGAIN; } @@ -395,32 +398,30 @@ static inline int __recursive_increment(atomic_int* mutex_value_ptr, int mvalue) // loop to update the counter. The counter will not overflow in the loop, // as only the owner thread can change it. // The mutex is still locked, so we don't need a release fence. - atomic_fetch_add_explicit(mutex_value_ptr, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); + atomic_fetch_add_explicit(&mutex->state, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return 0; } -static int __pthread_mutex_lock_with_timeout(pthread_mutex_t* mutex, +static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, const timespec* abs_timeout_or_null, clockid_t clock) { - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); + int old_state, mtype, tid, shared; - int mvalue, mtype, tid, shared; - - mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - mtype = (mvalue & MUTEX_TYPE_MASK); - shared = (mvalue & MUTEX_SHARED_MASK); + old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + mtype = (old_state & MUTEX_TYPE_MASK); + shared = (old_state & MUTEX_SHARED_MASK); // Handle common case first. if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) { - return __pthread_normal_mutex_lock(mutex_value_ptr, shared, abs_timeout_or_null, clock); + return __pthread_normal_mutex_lock(mutex, shared, abs_timeout_or_null, clock); } // Do we already own this recursive or error-check mutex? tid = __get_thread()->tid; - if (tid == MUTEX_OWNER_FROM_BITS(mvalue)) { + if (tid == MUTEX_OWNER_FROM_BITS(old_state)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EDEADLK; } - return __recursive_increment(mutex_value_ptr, mvalue); + return __recursive_increment(mutex, old_state); } const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; @@ -429,12 +430,12 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_t* mutex, // First, if the mutex is unlocked, try to quickly acquire it. // In the optimistic case where this works, set the state to locked_uncontended. - if (mvalue == unlocked) { - int newval = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; + if (old_state == unlocked) { + int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, - newval, memory_order_acquire, memory_order_relaxed))) { + if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, + new_state, memory_order_acquire, memory_order_relaxed))) { return 0; } } @@ -442,33 +443,33 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_t* mutex, ScopedTrace trace("Contending for pthread mutex"); while (true) { - if (mvalue == unlocked) { + if (old_state == unlocked) { // NOTE: We put the state to locked_contended since we _know_ there // is contention when we are in this loop. This ensures all waiters // will be unlocked. - int newval = MUTEX_OWNER_TO_BITS(tid) | locked_contended; + int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_contended; // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. - if (__predict_true(atomic_compare_exchange_weak_explicit(mutex_value_ptr, - &mvalue, newval, + if (__predict_true(atomic_compare_exchange_weak_explicit(&mutex->state, + &old_state, new_state, memory_order_acquire, memory_order_relaxed))) { return 0; } continue; - } else if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(mvalue)) { + } else if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(old_state)) { // We should set it to locked_contended beforing going to sleep. This can make // sure waiters will be woken up eventually. - int newval = MUTEX_STATE_BITS_FLIP_CONTENTION(mvalue); - if (__predict_false(!atomic_compare_exchange_weak_explicit(mutex_value_ptr, - &mvalue, newval, + int new_state = MUTEX_STATE_BITS_FLIP_CONTENTION(old_state); + if (__predict_false(!atomic_compare_exchange_weak_explicit(&mutex->state, + &old_state, new_state, memory_order_relaxed, memory_order_relaxed))) { continue; } - mvalue = newval; + old_state = new_state; } // We are in locked_contended state, sleep until someone wakes us up. @@ -480,54 +481,54 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_t* mutex, return ETIMEDOUT; } } - if (__futex_wait_ex(mutex_value_ptr, shared, mvalue, rel_timeout) == -ETIMEDOUT) { + if (__futex_wait_ex(&mutex->state, shared, old_state, rel_timeout) == -ETIMEDOUT) { return ETIMEDOUT; } - mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); + old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); } } -int pthread_mutex_lock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); +int pthread_mutex_lock(pthread_mutex_t* mutex_interface) { + pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - int mtype = (mvalue & MUTEX_TYPE_MASK); - int shared = (mvalue & MUTEX_SHARED_MASK); + int old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + int mtype = (old_state & MUTEX_TYPE_MASK); + int shared = (old_state & MUTEX_SHARED_MASK); // Avoid slowing down fast path of normal mutex lock operation. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - if (__predict_true(__pthread_normal_mutex_trylock(mutex_value_ptr, shared) == 0)) { + if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { return 0; } } return __pthread_mutex_lock_with_timeout(mutex, NULL, 0); } -int pthread_mutex_unlock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); +int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { + pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int mvalue, mtype, tid, shared; + int old_state, mtype, tid, shared; - mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - mtype = (mvalue & MUTEX_TYPE_MASK); - shared = (mvalue & MUTEX_SHARED_MASK); + old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + mtype = (old_state & MUTEX_TYPE_MASK); + shared = (old_state & MUTEX_SHARED_MASK); // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - __pthread_normal_mutex_unlock(mutex_value_ptr, shared); + __pthread_normal_mutex_unlock(mutex, shared); return 0; } // Do we already own this recursive or error-check mutex? tid = __get_thread()->tid; - if ( tid != MUTEX_OWNER_FROM_BITS(mvalue) ) + if ( tid != MUTEX_OWNER_FROM_BITS(old_state) ) return EPERM; // If the counter is > 0, we can simply decrement it atomically. // Since other threads can mutate the lower state bits (and only the // lower state bits), use a compare_exchange loop to do it. - if (!MUTEX_COUNTER_BITS_IS_ZERO(mvalue)) { + if (!MUTEX_COUNTER_BITS_IS_ZERO(old_state)) { // We still own the mutex, so a release fence is not needed. - atomic_fetch_sub_explicit(mutex_value_ptr, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); + atomic_fetch_sub_explicit(&mutex->state, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return 0; } @@ -538,36 +539,36 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) { // A release fence is required to make previous stores visible to next // lock owner threads. const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; - mvalue = atomic_exchange_explicit(mutex_value_ptr, unlocked, memory_order_release); - if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(mvalue)) { - __futex_wake_ex(mutex_value_ptr, shared, 1); + old_state = atomic_exchange_explicit(&mutex->state, unlocked, memory_order_release); + if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(old_state)) { + __futex_wake_ex(&mutex->state, shared, 1); } return 0; } -int pthread_mutex_trylock(pthread_mutex_t* mutex) { - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); +int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) { + pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int mvalue = atomic_load_explicit(mutex_value_ptr, memory_order_relaxed); - int mtype = (mvalue & MUTEX_TYPE_MASK); - int shared = (mvalue & MUTEX_SHARED_MASK); + int old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + int mtype = (old_state & MUTEX_TYPE_MASK); + int shared = (old_state & MUTEX_SHARED_MASK); const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; const int locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { - return __pthread_normal_mutex_trylock(mutex_value_ptr, shared); + return __pthread_normal_mutex_trylock(mutex, shared); } // Do we already own this recursive or error-check mutex? pid_t tid = __get_thread()->tid; - if (tid == MUTEX_OWNER_FROM_BITS(mvalue)) { + if (tid == MUTEX_OWNER_FROM_BITS(old_state)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EBUSY; } - return __recursive_increment(mutex_value_ptr, mvalue); + return __recursive_increment(mutex, old_state); } // Same as pthread_mutex_lock, except that we don't want to wait, and @@ -575,9 +576,9 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) { // lock if it is released / not owned by anyone. No need for a complex loop. // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. - mvalue = unlocked; - int newval = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; - if (__predict_true(atomic_compare_exchange_strong_explicit(mutex_value_ptr, &mvalue, newval, + old_state = unlocked; + int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; + if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, new_state, memory_order_acquire, memory_order_relaxed))) { return 0; @@ -586,7 +587,7 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex) { } #if !defined(__LP64__) -extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms) { +extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, unsigned ms) { timespec abs_timeout; clock_gettime(CLOCK_MONOTONIC, &abs_timeout); abs_timeout.tv_sec += ms / 1000; @@ -596,7 +597,8 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms abs_timeout.tv_nsec -= NS_PER_S; } - int error = __pthread_mutex_lock_with_timeout(mutex, &abs_timeout, CLOCK_MONOTONIC); + int error = __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface), + &abs_timeout, CLOCK_MONOTONIC); if (error == ETIMEDOUT) { error = EBUSY; } @@ -604,18 +606,19 @@ extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms } #endif -int pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout) { - return __pthread_mutex_lock_with_timeout(mutex, abs_timeout, CLOCK_REALTIME); +int pthread_mutex_timedlock(pthread_mutex_t* mutex_interface, const timespec* abs_timeout) { + return __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface), + abs_timeout, CLOCK_REALTIME); } -int pthread_mutex_destroy(pthread_mutex_t* mutex) { +int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) { // Use trylock to ensure that the mutex is valid and not already locked. - int error = pthread_mutex_trylock(mutex); + int error = pthread_mutex_trylock(mutex_interface); if (error != 0) { return error; } - atomic_int* mutex_value_ptr = get_mutex_value_pointer(mutex); - atomic_store_explicit(mutex_value_ptr, 0xdead10cc, memory_order_relaxed); + pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); + atomic_store_explicit(&mutex->state, 0xdead10cc, memory_order_relaxed); return 0; } diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 234a43d51..0ad14dd13 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -36,30 +36,15 @@ #include #include -#if defined(__LP64__) - #define __RESERVED_INITIALIZER , {0} -#else - #define __RESERVED_INITIALIZER -#endif - typedef struct { - int value; -#ifdef __LP64__ - char __reserved[36]; +#if defined(__LP64__) + int32_t __private[10]; +#else + int32_t __private[1]; #endif } pthread_mutex_t; -#define __PTHREAD_MUTEX_INIT_VALUE 0 -#define __PTHREAD_RECURSIVE_MUTEX_INIT_VALUE 0x4000 -#define __PTHREAD_ERRORCHECK_MUTEX_INIT_VALUE 0x8000 - -#define PTHREAD_MUTEX_INITIALIZER {__PTHREAD_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP {__PTHREAD_ERRORCHECK_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {__PTHREAD_RECURSIVE_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} - -/* TODO: remove this namespace pollution. */ -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +typedef long pthread_mutexattr_t; enum { PTHREAD_MUTEX_NORMAL = 0, @@ -72,28 +57,35 @@ enum { PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL }; +#define PTHREAD_MUTEX_INITIALIZER { { ((PTHREAD_MUTEX_NORMAL & 3) << 14) } } +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP { { ((PTHREAD_MUTEX_RECURSIVE & 3) << 14) } } +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP { { ((PTHREAD_MUTEX_ERRORCHECK & 3) << 14) } } + +/* TODO: remove this namespace pollution. */ +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + typedef struct { #if defined(__LP64__) - char __private[48]; + int32_t __private[12]; #else - char __private[4]; + int32_t __private[1]; #endif -} pthread_cond_t __attribute__((aligned(4))); +} pthread_cond_t; + +typedef long pthread_condattr_t; #define PTHREAD_COND_INITIALIZER { { 0 } } -typedef long pthread_mutexattr_t; -typedef long pthread_condattr_t; - -typedef long pthread_rwlockattr_t; - typedef struct { #if defined(__LP64__) - char __private[56]; + int32_t __private[14]; #else - char __private[40]; + int32_t __private[10]; #endif -} pthread_rwlock_t __attribute__((aligned(4))); +} pthread_rwlock_t; + +typedef long pthread_rwlockattr_t; #define PTHREAD_RWLOCK_INITIALIZER { { 0 } } diff --git a/libc/stdio/fileext.h b/libc/stdio/fileext.h index 209815aee..6cacc0f84 100644 --- a/libc/stdio/fileext.h +++ b/libc/stdio/fileext.h @@ -61,7 +61,11 @@ do { \ _UB(fp)._base = NULL; \ _UB(fp)._size = 0; \ WCIO_INIT(fp); \ - _FLOCK(fp).value = __PTHREAD_RECURSIVE_MUTEX_INIT_VALUE; \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(&_FLOCK(fp), &attr); \ + pthread_mutexattr_destroy(&attr); \ _EXT(fp)->_stdio_handles_locking = true; \ } while (0) diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index b4667f939..632e28552 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -16,10 +16,6 @@ #include -#include "private/ScopeGuard.h" -#include "BionicDeathTest.h" -#include "ScopedSignalHandler.h" - #include #include #include @@ -35,6 +31,11 @@ #include #include +#include "private/bionic_macros.h" +#include "private/ScopeGuard.h" +#include "BionicDeathTest.h" +#include "ScopedSignalHandler.h" + TEST(pthread, pthread_key_create) { pthread_key_t key; ASSERT_EQ(0, pthread_key_create(&key, NULL)); @@ -1221,54 +1222,84 @@ TEST(pthread, pthread_mutexattr_gettype) { ASSERT_EQ(0, pthread_mutexattr_destroy(&attr)); } -static void CreateMutex(pthread_mutex_t& mutex, int mutex_type) { - pthread_mutexattr_t attr; - ASSERT_EQ(0, pthread_mutexattr_init(&attr)); - ASSERT_EQ(0, pthread_mutexattr_settype(&attr, mutex_type)); - ASSERT_EQ(0, pthread_mutex_init(&mutex, &attr)); - ASSERT_EQ(0, pthread_mutexattr_destroy(&attr)); -} +struct PthreadMutex { + pthread_mutex_t lock; + + PthreadMutex(int mutex_type) { + init(mutex_type); + } + + ~PthreadMutex() { + destroy(); + } + + private: + void init(int mutex_type) { + pthread_mutexattr_t attr; + ASSERT_EQ(0, pthread_mutexattr_init(&attr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&attr, mutex_type)); + ASSERT_EQ(0, pthread_mutex_init(&lock, &attr)); + ASSERT_EQ(0, pthread_mutexattr_destroy(&attr)); + } + + void destroy() { + ASSERT_EQ(0, pthread_mutex_destroy(&lock)); + } + + DISALLOW_COPY_AND_ASSIGN(PthreadMutex); +}; TEST(pthread, pthread_mutex_lock_NORMAL) { - pthread_mutex_t lock; - CreateMutex(lock, PTHREAD_MUTEX_NORMAL); + PthreadMutex m(PTHREAD_MUTEX_NORMAL); - ASSERT_EQ(0, pthread_mutex_lock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_destroy(&lock)); + ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); } TEST(pthread, pthread_mutex_lock_ERRORCHECK) { - pthread_mutex_t lock; - CreateMutex(lock, PTHREAD_MUTEX_ERRORCHECK); + PthreadMutex m(PTHREAD_MUTEX_ERRORCHECK); - ASSERT_EQ(0, pthread_mutex_lock(&lock)); - ASSERT_EQ(EDEADLK, pthread_mutex_lock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_trylock(&lock)); - ASSERT_EQ(EBUSY, pthread_mutex_trylock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(EPERM, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_destroy(&lock)); + ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); + ASSERT_EQ(EDEADLK, pthread_mutex_lock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_trylock(&m.lock)); + ASSERT_EQ(EBUSY, pthread_mutex_trylock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); + ASSERT_EQ(EPERM, pthread_mutex_unlock(&m.lock)); } TEST(pthread, pthread_mutex_lock_RECURSIVE) { - pthread_mutex_t lock; - CreateMutex(lock, PTHREAD_MUTEX_RECURSIVE); + PthreadMutex m(PTHREAD_MUTEX_RECURSIVE); - ASSERT_EQ(0, pthread_mutex_lock(&lock)); - ASSERT_EQ(0, pthread_mutex_lock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_trylock(&lock)); - ASSERT_EQ(0, pthread_mutex_unlock(&lock)); - ASSERT_EQ(EPERM, pthread_mutex_unlock(&lock)); - ASSERT_EQ(0, pthread_mutex_destroy(&lock)); + ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_trylock(&m.lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); + ASSERT_EQ(EPERM, pthread_mutex_unlock(&m.lock)); +} + +TEST(pthread, pthread_mutex_init_same_as_static_initializers) { + pthread_mutex_t lock_normal = PTHREAD_MUTEX_INITIALIZER; + PthreadMutex m1(PTHREAD_MUTEX_NORMAL); + ASSERT_EQ(0, memcmp(&lock_normal, &m1.lock, sizeof(pthread_mutex_t))); + pthread_mutex_destroy(&lock_normal); + + pthread_mutex_t lock_errorcheck = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + PthreadMutex m2(PTHREAD_MUTEX_ERRORCHECK); + ASSERT_EQ(0, memcmp(&lock_errorcheck, &m2.lock, sizeof(pthread_mutex_t))); + pthread_mutex_destroy(&lock_errorcheck); + + pthread_mutex_t lock_recursive = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + PthreadMutex m3(PTHREAD_MUTEX_RECURSIVE); + ASSERT_EQ(0, memcmp(&lock_recursive, &m3.lock, sizeof(pthread_mutex_t))); + ASSERT_EQ(0, pthread_mutex_destroy(&lock_recursive)); } class MutexWakeupHelper { private: - pthread_mutex_t mutex; + PthreadMutex m; enum Progress { LOCK_INITIALIZED, LOCK_WAITING, @@ -1281,17 +1312,19 @@ class MutexWakeupHelper { ASSERT_EQ(LOCK_INITIALIZED, helper->progress); helper->progress = LOCK_WAITING; - ASSERT_EQ(0, pthread_mutex_lock(&helper->mutex)); + ASSERT_EQ(0, pthread_mutex_lock(&helper->m.lock)); ASSERT_EQ(LOCK_RELEASED, helper->progress); - ASSERT_EQ(0, pthread_mutex_unlock(&helper->mutex)); + ASSERT_EQ(0, pthread_mutex_unlock(&helper->m.lock)); helper->progress = LOCK_ACCESSED; } public: - void test(int mutex_type) { - CreateMutex(mutex, mutex_type); - ASSERT_EQ(0, pthread_mutex_lock(&mutex)); + MutexWakeupHelper(int mutex_type) : m(mutex_type) { + } + + void test() { + ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); progress = LOCK_INITIALIZED; pthread_t thread; @@ -1303,27 +1336,26 @@ class MutexWakeupHelper { } usleep(5000); progress = LOCK_RELEASED; - ASSERT_EQ(0, pthread_mutex_unlock(&mutex)); + ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); ASSERT_EQ(0, pthread_join(thread, NULL)); ASSERT_EQ(LOCK_ACCESSED, progress); - ASSERT_EQ(0, pthread_mutex_destroy(&mutex)); } }; TEST(pthread, pthread_mutex_NORMAL_wakeup) { - MutexWakeupHelper helper; - helper.test(PTHREAD_MUTEX_NORMAL); + MutexWakeupHelper helper(PTHREAD_MUTEX_NORMAL); + helper.test(); } TEST(pthread, pthread_mutex_ERRORCHECK_wakeup) { - MutexWakeupHelper helper; - helper.test(PTHREAD_MUTEX_ERRORCHECK); + MutexWakeupHelper helper(PTHREAD_MUTEX_ERRORCHECK); + helper.test(); } TEST(pthread, pthread_mutex_RECURSIVE_wakeup) { - MutexWakeupHelper helper; - helper.test(PTHREAD_MUTEX_RECURSIVE); + MutexWakeupHelper helper(PTHREAD_MUTEX_RECURSIVE); + helper.test(); } TEST(pthread, pthread_mutex_owner_tid_limit) { From df79c330d895af31f39ee301dee62731fa586168 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 25 Mar 2015 17:38:10 -0700 Subject: [PATCH 031/123] Initial implementation of __cxa_thread_atexit_impl This is initial implementations; does not yet handle dlclose - undefined behavior, needs linker support to handle it right. Bug: 19800080 Bug: 16696563 Change-Id: I7a3e21ed7f7ec01e62ea1b7cb2ab253590ea0686 --- libc/Android.mk | 22 +++++++ libc/bionic/__cxa_thread_atexit_impl.cpp | 48 +++++++++++++++ libc/bionic/pthread_exit.cpp | 6 +- tests/Android.mk | 2 + tests/__cxa_thread_atexit_test.cpp | 74 ++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 libc/bionic/__cxa_thread_atexit_impl.cpp create mode 100644 tests/__cxa_thread_atexit_test.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 6f430ccfc..ebc59ded7 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -533,6 +533,9 @@ libc_pthread_src_files := \ bionic/pthread_setschedparam.cpp \ bionic/pthread_sigmask.cpp \ +libc_thread_atexit_impl_src_files := \ + bionic/__cxa_thread_atexit_impl.cpp \ + libc_arch_static_src_files := \ bionic/dl_iterate_phdr_static.cpp \ @@ -1002,6 +1005,24 @@ $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags)) $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_src_files)) include $(BUILD_STATIC_LIBRARY) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files) +LOCAL_CFLAGS := $(libc_common_cflags) -fno-data-sections -Wframe-larger-than=2048 + +LOCAL_CONLYFLAGS := $(libc_common_conlyflags) +LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast +LOCAL_C_INCLUDES := $(libc_common_c_includes) +LOCAL_MODULE := libc_thread_atexit_impl +# TODO: Clang tries to use __tls_get_addr which is not supported yet +# remove after it is implemented. +LOCAL_CLANG := false +LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) +LOCAL_CXX_STL := none +LOCAL_SYSTEM_SHARED_LIBRARIES := +LOCAL_ADDRESS_SANITIZER := false +LOCAL_NATIVE_COVERAGE := $(bionic_coverage) + +include $(BUILD_STATIC_LIBRARY) # ======================================================== # libc_pthread.a - pthreads parts that previously lived in @@ -1206,6 +1227,7 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \ libc_pthread \ libc_stack_protector \ libc_syscalls \ + libc_thread_atexit_impl \ libc_tzcode \ LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp new file mode 100644 index 000000000..9ae6dfda1 --- /dev/null +++ b/libc/bionic/__cxa_thread_atexit_impl.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +struct thread_local_dtor { + void (*func) (void *); + void *arg; + void *dso_handle; // unused... + thread_local_dtor* next; +}; + +__thread thread_local_dtor* thread_local_dtors = nullptr; + +extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) { + thread_local_dtor* dtor = new thread_local_dtor(); + + dtor->func = func; + dtor->arg = arg; + dtor->dso_handle = dso_handle; + dtor->next = thread_local_dtors; + + thread_local_dtors = dtor; + + return 0; +} + +extern "C" __LIBC_HIDDEN__ void __cxa_thread_finalize() { + while (thread_local_dtors != nullptr) { + thread_local_dtor* current = thread_local_dtors; + thread_local_dtors = current->next; + + current->func(current->arg); + delete current; + } +} diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index c2232a939..1de85f510 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -37,6 +37,7 @@ extern "C" __noreturn void _exit_with_stack_teardown(void*, size_t); extern "C" __noreturn void __exit(int); extern "C" int __set_tid_address(int*); +extern "C" void __cxa_thread_finalize(); /* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions * and thread cancelation @@ -59,10 +60,13 @@ void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) { } void pthread_exit(void* return_value) { + // Call dtors for thread_local objects first. + __cxa_thread_finalize(); + pthread_internal_t* thread = __get_thread(); thread->return_value = return_value; - // Call the cleanup handlers first. + // Call the cleanup handlers. while (thread->cleanup_stack) { __pthread_cleanup_t* c = thread->cleanup_stack; thread->cleanup_stack = c->__cleanup_prev; diff --git a/tests/Android.mk b/tests/Android.mk index 0a83e8478..8804b71b8 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -258,10 +258,12 @@ bionic-unit-tests_static_libraries := \ libtinyxml2 \ liblog \ +# TODO: Include __cxa_thread_atexit_test.cpp to glibc tests once it is upgraded (glibc 2.18+) bionic-unit-tests_src_files := \ atexit_test.cpp \ dl_test.cpp \ dlext_test.cpp \ + __cxa_thread_atexit_test.cpp \ dlfcn_test.cpp \ bionic-unit-tests_cflags := $(test_cflags) diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp new file mode 100644 index 000000000..017731498 --- /dev/null +++ b/tests/__cxa_thread_atexit_test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle); + +static void thread_atexit_fn1(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "one, "; +} + +static void thread_atexit_fn2(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "two, "; +} + +static void thread_atexit_from_atexit(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "oops, "; +} + +static void thread_atexit_fn3(void* arg) { + __cxa_thread_atexit_impl(thread_atexit_from_atexit, arg, nullptr); + std::string* call_sequence = static_cast(arg); + *call_sequence += "three, "; +} + +static void thread_atexit_fn4(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "four, "; +} + +static void thread_atexit_fn5(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "five."; +} + +static void* thread_main(void* arg) { + __cxa_thread_atexit_impl(thread_atexit_fn5, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn4, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn3, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn2, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn1, arg, nullptr); + return nullptr; +} + +TEST(__cxa_thread_atexit_impl, smoke) { + std::string atexit_call_sequence; + + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, nullptr, thread_main, &atexit_call_sequence)); + ASSERT_EQ(0, pthread_join(t, nullptr)); + ASSERT_EQ("one, two, three, oops, four, five.", atexit_call_sequence); +} + + From 377243b78c43ffc98a6c9f145a6cd27ab99c08f0 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 25 Mar 2015 19:59:00 -0700 Subject: [PATCH 032/123] Remove PTHREAD_RECURSIVE_MUTEX_INITIALIZER and PTHREAD_ERRORCHECK_MUTEX_INITIALIZER. These macros are also not used in glibc. And we should use PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP and PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP instead. Change-Id: I35195e2f499712dcde9305bbb93622d0f7ca874b --- libc/include/pthread.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 0ad14dd13..83a56d6f4 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -61,10 +61,6 @@ enum { #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP { { ((PTHREAD_MUTEX_RECURSIVE & 3) << 14) } } #define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP { { ((PTHREAD_MUTEX_ERRORCHECK & 3) << 14) } } -/* TODO: remove this namespace pollution. */ -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - typedef struct { #if defined(__LP64__) int32_t __private[12]; From 569a75268948e6ee8c1b2faf1f77877d4d3c678f Mon Sep 17 00:00:00 2001 From: Simon Baldwin Date: Thu, 26 Mar 2015 17:13:19 +0000 Subject: [PATCH 033/123] Fix comment drift in assorted relocation packer modules. Some of the commentary in relocation packer code is relevant only to the packing strategy employed by chromium, and no longer applies here. This change fixes or deletes it. Code comment change only; no functional effect. Change-Id: Id229ee1d802bba608be15b79bc75bf90df557dab Signed-off-by: Simon Baldwin --- tools/relocation_packer/src/elf_file.h | 45 +++----------------------- tools/relocation_packer/src/leb128.h | 5 ++- tools/relocation_packer/src/main.cc | 3 -- tools/relocation_packer/src/packer.h | 37 --------------------- tools/relocation_packer/src/sleb128.h | 5 ++- 5 files changed, 8 insertions(+), 87 deletions(-) diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h index 73c31923f..a749d5066 100644 --- a/tools/relocation_packer/src/elf_file.h +++ b/tools/relocation_packer/src/elf_file.h @@ -4,53 +4,16 @@ // ELF shared object file updates handler. // -// Provides functions to remove relative relocations from the .rel.dyn -// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn, -// and unpack to return the file to its pre-packed state. -// -// Files to be packed or unpacked must include an existing .android.rel.dyn -// or android.rela.dyn section. A standard libchrome..so will not -// contain this section, so the following can be used to add one: -// -// echo -n 'NULL' >/tmp/small -// if file libchrome..so | grep -q 'ELF 32'; then -// arm-linux-androideabi-objcopy -// --add-section .android.rel.dyn=/tmp/small -// libchrome..so libchrome..so.packed -// else -// aarch64-linux-android-objcopy -// --add-section .android.rela.dyn=/tmp/small -// libchrome..so libchrome..so.packed -// fi -// rm /tmp/small -// -// To use, open the file and pass the file descriptor to the constructor, -// then pack or unpack as desired. Packing or unpacking will flush the file -// descriptor on success. Example: -// -// int fd = open(..., O_RDWR); -// ElfFile elf_file(fd); -// bool status; -// if (is_packing) -// status = elf_file.PackRelocations(); -// else -// status = elf_file.UnpackRelocations(); -// close(fd); +// Provides functions to pack relocations in the .rel.dyn or .rela.dyn +// sections, and unpack to return the file to its pre-packed state. // // SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with // NONE-type entries rather than cutting a hole out of the shared object // file. This keeps all load addresses and offsets constant, and enables // easier debugging and testing. // -// A packed shared object file has all of its relative relocations -// removed from .rel.dyn or .rela.dyn, and replaced as packed data in -// .android.rel.dyn or .android.rela.dyn respectively. The resulting file -// is shorter than its non-packed original. -// -// Unpacking a packed file restores the file to its non-packed state, by -// expanding the packed data in .android.rel.dyn or .android.rela.dyn, -// combining the relative relocations with the data already in .rel.dyn -// or .rela.dyn, and then writing back the now expanded section. +// A packed shared object file is shorter than its non-packed original. +// Unpacking a packed file restores the file to its non-packed state. #ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ #define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h index 2c5b5d079..67fc4b829 100644 --- a/tools/relocation_packer/src/leb128.h +++ b/tools/relocation_packer/src/leb128.h @@ -4,9 +4,8 @@ // LEB128 encoder and decoder for packed relative relocations. // -// Run-length encoded relative relocations consist of a large number -// of pairs of relatively small positive integer values. Encoding these as -// LEB128 saves space. +// Packed relocations consist of a large number of relatively small +// integer values. Encoding these as LEB128 saves space. // // For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc index 3f784e4f3..8e9de6d96 100644 --- a/tools/relocation_packer/src/main.cc +++ b/tools/relocation_packer/src/main.cc @@ -4,9 +4,6 @@ // Tool to pack and unpack relative relocations in a shared library. // -// Packing removes relative relocations from .rel.dyn and writes them -// in a more compact form to .android.rel.dyn. Unpacking does the reverse. -// // Invoke with -v to trace actions taken when packing or unpacking. // Invoke with -p to pad removed relocations with R_*_NONE. Suppresses // shrinking of .rel.dyn. diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h index 8a57e623b..63f50e249 100644 --- a/tools/relocation_packer/src/packer.h +++ b/tools/relocation_packer/src/packer.h @@ -3,43 +3,6 @@ // found in the LICENSE file. // Pack relative relocations into a more compact form. -// -// -// For relative relocations without addends (32 bit platforms) -// ----------------------------------------------------------- -// -// Applies two packing strategies. The first is run-length encoding, which -// turns a large set of relative relocations into a much smaller set -// of delta-count pairs, prefixed with a two-word header comprising the -// count of pairs and the initial relocation offset. The second is LEB128 -// encoding, which compresses the result of run-length encoding. -// -// Once packed, data is prefixed by an identifier that allows for any later -// versioning of packing strategies. -// -// A complete packed stream of relocations without addends might look -// something like: -// -// "APR1" pairs init_offset count1 delta1 count2 delta2 ... -// 41505231 f2b003 b08ac716 e001 04 01 10 ... -// -// -// For relative relocations with addends (64 bit platforms) -// -------------------------------------------------------- -// -// Applies two packing strategies. The first is delta encoding, which -// turns a large set of relative relocations into a smaller set -// of offset and addend delta pairs, prefixed with a header indicating the -// count of pairs. The second is signed LEB128 encoding, which compacts -// the result of delta encoding. -// -// Once packed, data is prefixed by an identifier that allows for any later -// versioning of packing strategies. -// -// A complete packed stream might look something like: -// -// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ... -// 41505232 f2b018 04 28 08 9f01 ... #ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ #define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h index fa0a24610..3a63f6679 100644 --- a/tools/relocation_packer/src/sleb128.h +++ b/tools/relocation_packer/src/sleb128.h @@ -4,9 +4,8 @@ // SLEB128 encoder and decoder for packed relative relocations. // -// Delta encoded relative relocations consist of a large number -// of pairs signed integer values, many with small values. Encoding these -// as signed LEB128 saves space. +// Packed relocations consist of a large number of relatively small +// integer values. Encoding these as LEB128 saves space. // // For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. From 220b99bdc1c5f51825ac2a87062bc05fe3e0d722 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 26 Mar 2015 18:13:07 +0000 Subject: [PATCH 034/123] Revert "Cause Fatal error when invalid pthread_id is detected." Some code like in https://buganizer.corp.google.com/u/0/issues/19942911 need to change first. This reverts commit 03324780aae9ff28c8acf52debf0ea39120e5ab8. Change-Id: I13ff1e5b3d0672bae9cde234ffba32fbbf33d338 --- libc/bionic/pthread_internal.cpp | 12 +++------ tests/pthread_test.cpp | 46 +++++--------------------------- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp index afafad84c..1967ccf8c 100644 --- a/libc/bionic/pthread_internal.cpp +++ b/libc/bionic/pthread_internal.cpp @@ -81,16 +81,12 @@ void __pthread_internal_remove_and_free(pthread_internal_t* thread) { pthread_internal_t* __pthread_internal_find(pthread_t thread_id) { pthread_internal_t* thread = reinterpret_cast(thread_id); - { - ScopedPthreadMutexLocker locker(&g_thread_list_lock); + ScopedPthreadMutexLocker locker(&g_thread_list_lock); - for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) { - if (t == thread) { - return thread; - } + for (pthread_internal_t* t = g_thread_list; t != NULL; t = t->next) { + if (t == thread) { + return thread; } } - // TODO: Remove g_thread_list/g_thread_list_lock when the fatal error below doesn't happen. - __libc_fatal("No such thread: %p", thread); return NULL; } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index b4667f939..4eb352d8e 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -399,17 +399,12 @@ TEST(pthread, pthread_setname_np__other) { ASSERT_EQ(0, pthread_setname_np(t1, "short 2")); } -TEST_F(pthread_DeathTest, pthread_setname_np__no_such_thread) { +TEST(pthread, pthread_setname_np__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); // Call pthread_setname_np after thread has already exited. -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_setname_np(dead_thread, "short 3"), testing::KilledBySignal(SIGABRT), - "No such thread"); -#else ASSERT_EQ(ENOENT, pthread_setname_np(dead_thread, "short 3")); -#endif } TEST(pthread, pthread_kill__0) { @@ -435,15 +430,11 @@ TEST(pthread, pthread_kill__in_signal_handler) { ASSERT_EQ(0, pthread_kill(pthread_self(), SIGALRM)); } -TEST_F(pthread_DeathTest, pthread_detach__no_such_thread) { +TEST(pthread, pthread_detach__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_detach(dead_thread), testing::KilledBySignal(SIGABRT), "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_detach(dead_thread)); -#endif } TEST(pthread, pthread_detach_no_leak) { @@ -494,67 +485,44 @@ TEST(pthread, pthread_getcpuclockid__clock_gettime) { ASSERT_EQ(0, clock_gettime(c, &ts)); } -TEST_F(pthread_DeathTest, pthread_getcpuclockid__no_such_thread) { +TEST(pthread, pthread_getcpuclockid__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); clockid_t c; -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_getcpuclockid(dead_thread, &c), testing::KilledBySignal(SIGABRT), - "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_getcpuclockid(dead_thread, &c)); -#endif } -TEST_F(pthread_DeathTest, pthread_getschedparam__no_such_thread) { +TEST(pthread, pthread_getschedparam__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); int policy; sched_param param; -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_getschedparam(dead_thread, &policy, ¶m), testing::KilledBySignal(SIGABRT), - "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_getschedparam(dead_thread, &policy, ¶m)); -#endif } -TEST_F(pthread_DeathTest, pthread_setschedparam__no_such_thread) { +TEST(pthread, pthread_setschedparam__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); int policy = 0; sched_param param; -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_setschedparam(dead_thread, policy, ¶m), testing::KilledBySignal(SIGABRT), - "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_setschedparam(dead_thread, policy, ¶m)); -#endif } -TEST_F(pthread_DeathTest, pthread_join__no_such_thread) { +TEST(pthread, pthread_join__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_join(dead_thread, NULL), testing::KilledBySignal(SIGABRT), "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_join(dead_thread, NULL)); -#endif } -TEST_F(pthread_DeathTest, pthread_kill__no_such_thread) { +TEST(pthread, pthread_kill__no_such_thread) { pthread_t dead_thread; MakeDeadThread(dead_thread); -#if defined(__BIONIC__) - ASSERT_EXIT(pthread_kill(dead_thread, 0), testing::KilledBySignal(SIGABRT), "No such thread"); -#else ASSERT_EQ(ESRCH, pthread_kill(dead_thread, 0)); -#endif } TEST(pthread, pthread_join__multijoin) { From 797bffb76095c5bf83b486579dab9db7bb4fe09e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 26 Mar 2015 16:47:18 -0700 Subject: [PATCH 035/123] Explain why we need objcopy for the linker Change-Id: I646673abc6095b56f72add493b60925375b75b78 --- linker/Android.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linker/Android.mk b/linker/Android.mk index 81c007f3e..5bdc2f901 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -70,7 +70,9 @@ LOCAL_MULTILIB := both # meaningful name resolution. LOCAL_STRIP_MODULE := keep_symbols -# Insert an extra objcopy step to add prefix to symbols. +# Insert an extra objcopy step to add prefix to symbols. This is needed to prevent gdb +# looking up symbols in the linker by mistake. +# # Note we are using "=" instead of ":=" to defer the evaluation, # because LOCAL_2ND_ARCH_VAR_PREFIX or linked_module isn't set properly yet at this point. LOCAL_POST_LINK_CMD = $(hide) $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJCOPY) \ From 947adedebc480917b8490bc16e8b2d82b441095a Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 26 Mar 2015 11:07:04 -0700 Subject: [PATCH 036/123] Add test for thread_local keyword. For gcc only for the time being. Bug: 19800080 Bug: 16696563 Change-Id: Ifaa59a131ca2d9030554cee7ce631dcb1d081938 --- tests/Android.mk | 4 ++++ tests/__cxa_thread_atexit_test.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/Android.mk b/tests/Android.mk index 8804b71b8..995877eaf 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -287,6 +287,10 @@ bionic-unit-tests_shared_libraries_target := \ libdl_preempt_test_1 \ libdl_preempt_test_2 +# TODO: clang support for thread_local on arm is done via __aeabi_read_tp() +# which bionic does not support. Reenable this once this question is resolved. +bionic-unit-tests_clang_target := false + ifneq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH)) bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global endif diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp index 017731498..fea60b719 100644 --- a/tests/__cxa_thread_atexit_test.cpp +++ b/tests/__cxa_thread_atexit_test.cpp @@ -20,6 +20,36 @@ #include +static std::string class_with_dtor_output; + +class ClassWithDtor { + public: + void set_message(const std::string& msg) { + message = msg; + } + + ~ClassWithDtor() { + class_with_dtor_output += message; + } + private: + std::string message; +}; + +thread_local ClassWithDtor class_with_dtor; + +static void* thread_nop(void* arg) { + class_with_dtor.set_message(*static_cast(arg)); + return nullptr; +} + +TEST(thread_local, smoke) { + std::string msg("dtor called."); + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, nullptr, thread_nop, &msg)); + ASSERT_EQ(0, pthread_join(t, nullptr)); + ASSERT_EQ("dtor called.", class_with_dtor_output); +} + extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle); static void thread_atexit_fn1(void* arg) { From 402d19945092eee87cfb6c029c98ccb4fb40d2c6 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 27 Mar 2015 15:27:07 -0700 Subject: [PATCH 037/123] Fix libstdc++.so hash-style. Bug: 19059885 Bug: 19958712 Change-Id: I167457a54cc688d64912a50f5fb75e4e2f3e3937 --- libc/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/Android.mk b/libc/Android.mk index ebc59ded7..e438552eb 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1502,7 +1502,7 @@ LOCAL_CFLAGS := $(libc_common_cflags) LOCAL_CPPFLAGS := $(libc_common_cppflags) # TODO: This is to work around b/19059885. Remove after root cause is fixed -LOCAL_LDFLAGS_arm := -Wl,--hash-style=both +LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv LOCAL_SRC_FILES := $(libstdcxx_common_src_files) LOCAL_MODULE:= libstdc++ From e001ca3e18b085661868be9a77bd18bea587780c Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 27 Mar 2015 14:01:00 -0700 Subject: [PATCH 038/123] linker_environ: clear MALLOC_CONF when AT_SECURE=1 Clear JE_MALLOC_CONF and MALLOC_CONF Change-Id: Ia76e263783194ecaa362e8ccafbe13a28a4a1ba6 --- linker/linker_environ.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linker/linker_environ.cpp b/linker/linker_environ.cpp index 7272f4e83..9a0f0099f 100644 --- a/linker/linker_environ.cpp +++ b/linker/linker_environ.cpp @@ -115,6 +115,7 @@ static bool __is_unsafe_environment_variable(const char* name) { "GCONV_PATH", "GETCONF_DIR", "HOSTALIASES", + "JE_MALLOC_CONF", "LD_AOUT_LIBRARY_PATH", "LD_AOUT_PRELOAD", "LD_AUDIT", @@ -130,6 +131,7 @@ static bool __is_unsafe_environment_variable(const char* name) { "LOCALDOMAIN", "LOCPATH", "MALLOC_CHECK_", + "MALLOC_CONF", "MALLOC_TRACE", "NIS_PATH", "NLSPATH", From 595752f623ae88f7e4193a6e531a0805f1c6c4dc Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sun, 29 Mar 2015 00:03:55 -0400 Subject: [PATCH 039/123] add guard pages to the internal signal stacks Signal handlers tend to be lean, but can still overflow the (tiny) stack. Change-Id: Ia21c6453d92a9f8d1536ad01ff26a1a84c05f8fb --- libc/bionic/pthread_create.cpp | 18 ++++++++++++------ libc/bionic/pthread_exit.cpp | 2 +- libc/bionic/pthread_internal.h | 3 +++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 66632c494..179ae95da 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -69,17 +69,23 @@ void __init_tls(pthread_internal_t* thread) { void __init_alternate_signal_stack(pthread_internal_t* thread) { // Create and set an alternate signal stack. - stack_t ss; - ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (ss.ss_sp != MAP_FAILED) { - ss.ss_size = SIGSTKSZ; + void* stack_base = mmap(NULL, SIGNAL_STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (stack_base != MAP_FAILED) { + // Create a guard page to catch stack overflows in signal handlers. + if (mprotect(stack_base, PAGE_SIZE, PROT_NONE) == -1) { + munmap(stack_base, SIGNAL_STACK_SIZE); + return; + } + stack_t ss; + ss.ss_sp = stack_base + PAGE_SIZE; + ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; ss.ss_flags = 0; sigaltstack(&ss, NULL); - thread->alternate_signal_stack = ss.ss_sp; + thread->alternate_signal_stack = stack_base; // We can only use const static allocated string for mapped region name, as Android kernel // uses the string pointer directly when dumping /proc/pid/maps. - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ss.ss_sp, ss.ss_size, "thread signal stack"); + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, stack_base, SIGNAL_STACK_SIZE, "thread signal stack"); } } diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index 1de85f510..ceda93166 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -87,7 +87,7 @@ void pthread_exit(void* return_value) { sigaltstack(&ss, NULL); // Free it. - munmap(thread->alternate_signal_stack, SIGSTKSZ); + munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE); thread->alternate_signal_stack = NULL; } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 2151e03ed..3b91e6a19 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -130,6 +130,9 @@ __LIBC_HIDDEN__ void pthread_key_clean_all(void); */ #define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ) +/* Leave room for a guard page in the internally created signal stacks. */ +#define SIGNAL_STACK_SIZE (SIGSTKSZ + PAGE_SIZE) + /* Needed by fork. */ __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare(); __LIBC_HIDDEN__ extern void __bionic_atfork_run_child(); From 16c77212792808b9e4d8229e64c5b42f4327b6dc Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 30 Mar 2015 14:33:02 -0700 Subject: [PATCH 040/123] Fix clang build breakage ("arithmetic on a pointer to void"). Change-Id: Ia0953fc1cd0f8ea2d4423b3c6e34f6dc7a9f31e9 --- libc/bionic/pthread_create.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 179ae95da..cc37f5a74 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -77,7 +77,7 @@ void __init_alternate_signal_stack(pthread_internal_t* thread) { return; } stack_t ss; - ss.ss_sp = stack_base + PAGE_SIZE; + ss.ss_sp = reinterpret_cast(stack_base) + PAGE_SIZE; ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; ss.ss_flags = 0; sigaltstack(&ss, NULL); From 3925f32ffb1ac93fb20b0e4ec86aa600eb942645 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 31 Mar 2015 02:41:20 +0000 Subject: [PATCH 041/123] Revert "Fix clang build breakage ("arithmetic on a pointer to void")." This reverts commit 16c77212792808b9e4d8229e64c5b42f4327b6dc. Change-Id: I568dee5400599693b1585ce6d4be7a0b5f37dc74 --- libc/bionic/pthread_create.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index cc37f5a74..179ae95da 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -77,7 +77,7 @@ void __init_alternate_signal_stack(pthread_internal_t* thread) { return; } stack_t ss; - ss.ss_sp = reinterpret_cast(stack_base) + PAGE_SIZE; + ss.ss_sp = stack_base + PAGE_SIZE; ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; ss.ss_flags = 0; sigaltstack(&ss, NULL); From a3125fd1396a09a7fc4872dc4653f342150a3deb Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 31 Mar 2015 02:42:39 +0000 Subject: [PATCH 042/123] Revert "add guard pages to the internal signal stacks" This reverts commit 595752f623ae88f7e4193a6e531a0805f1c6c4dc. Change-Id: Iefa66e9049ca0424e53cd5fc320d161b93556dcb --- libc/bionic/pthread_create.cpp | 18 ++++++------------ libc/bionic/pthread_exit.cpp | 2 +- libc/bionic/pthread_internal.h | 3 --- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 179ae95da..66632c494 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -69,23 +69,17 @@ void __init_tls(pthread_internal_t* thread) { void __init_alternate_signal_stack(pthread_internal_t* thread) { // Create and set an alternate signal stack. - void* stack_base = mmap(NULL, SIGNAL_STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (stack_base != MAP_FAILED) { - // Create a guard page to catch stack overflows in signal handlers. - if (mprotect(stack_base, PAGE_SIZE, PROT_NONE) == -1) { - munmap(stack_base, SIGNAL_STACK_SIZE); - return; - } - stack_t ss; - ss.ss_sp = stack_base + PAGE_SIZE; - ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; + stack_t ss; + ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp != MAP_FAILED) { + ss.ss_size = SIGSTKSZ; ss.ss_flags = 0; sigaltstack(&ss, NULL); - thread->alternate_signal_stack = stack_base; + thread->alternate_signal_stack = ss.ss_sp; // We can only use const static allocated string for mapped region name, as Android kernel // uses the string pointer directly when dumping /proc/pid/maps. - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, stack_base, SIGNAL_STACK_SIZE, "thread signal stack"); + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ss.ss_sp, ss.ss_size, "thread signal stack"); } } diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index ceda93166..1de85f510 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -87,7 +87,7 @@ void pthread_exit(void* return_value) { sigaltstack(&ss, NULL); // Free it. - munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE); + munmap(thread->alternate_signal_stack, SIGSTKSZ); thread->alternate_signal_stack = NULL; } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 3b91e6a19..2151e03ed 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -130,9 +130,6 @@ __LIBC_HIDDEN__ void pthread_key_clean_all(void); */ #define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ) -/* Leave room for a guard page in the internally created signal stacks. */ -#define SIGNAL_STACK_SIZE (SIGSTKSZ + PAGE_SIZE) - /* Needed by fork. */ __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare(); __LIBC_HIDDEN__ extern void __bionic_atfork_run_child(); From ef115003012f61cf5539fdfeb201b98e4a92f610 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 30 Mar 2015 20:03:57 -0700 Subject: [PATCH 043/123] Revert "Revert "add guard pages to the internal signal stacks"" This reverts commit a3125fd1396a09a7fc4872dc4653f342150a3deb. And Fix the prctl() problem that cause system crash. Change-Id: Icc8d12d848cfba881a7984ca2827fd81be41f9fd --- libc/bionic/pthread_create.cpp | 17 ++++++++++++----- libc/bionic/pthread_exit.cpp | 2 +- libc/bionic/pthread_internal.h | 3 +++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 66632c494..dbdb18058 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -69,13 +69,20 @@ void __init_tls(pthread_internal_t* thread) { void __init_alternate_signal_stack(pthread_internal_t* thread) { // Create and set an alternate signal stack. - stack_t ss; - ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (ss.ss_sp != MAP_FAILED) { - ss.ss_size = SIGSTKSZ; + void* stack_base = mmap(NULL, SIGNAL_STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (stack_base != MAP_FAILED) { + + // Create a guard page to catch stack overflows in signal handlers. + if (mprotect(stack_base, PAGE_SIZE, PROT_NONE) == -1) { + munmap(stack_base, SIGNAL_STACK_SIZE); + return; + } + stack_t ss; + ss.ss_sp = reinterpret_cast(stack_base) + PAGE_SIZE; + ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; ss.ss_flags = 0; sigaltstack(&ss, NULL); - thread->alternate_signal_stack = ss.ss_sp; + thread->alternate_signal_stack = stack_base; // We can only use const static allocated string for mapped region name, as Android kernel // uses the string pointer directly when dumping /proc/pid/maps. diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index 1de85f510..ceda93166 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -87,7 +87,7 @@ void pthread_exit(void* return_value) { sigaltstack(&ss, NULL); // Free it. - munmap(thread->alternate_signal_stack, SIGSTKSZ); + munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE); thread->alternate_signal_stack = NULL; } diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 2151e03ed..3b91e6a19 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -130,6 +130,9 @@ __LIBC_HIDDEN__ void pthread_key_clean_all(void); */ #define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ) +/* Leave room for a guard page in the internally created signal stacks. */ +#define SIGNAL_STACK_SIZE (SIGSTKSZ + PAGE_SIZE) + /* Needed by fork. */ __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare(); __LIBC_HIDDEN__ extern void __bionic_atfork_run_child(); From 6170693e28dd72a1517c267f3f62b3f37477b8bb Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 31 Mar 2015 10:56:58 -0700 Subject: [PATCH 044/123] Make ThreadLocalBuffer a class rather than a macro. Bug: 19995392 Change-Id: I497c512648fbe66257da3fb3bcd5c9911f983705 --- libc/bionic/libgen.cpp | 18 ++++++------- libc/bionic/mntent.cpp | 11 ++++---- libc/bionic/pty.cpp | 16 ++++++------ libc/bionic/strerror.cpp | 10 ++++---- libc/bionic/strsignal.cpp | 6 ++--- libc/bionic/stubs.cpp | 30 +++++++++------------- libc/dns/resolv/res_state.c | 8 +++--- libc/private/ThreadLocalBuffer.h | 44 +++++++++++++++----------------- libc/private/bionic_tls.h | 30 ++++++++++++---------- tests/pthread_test.cpp | 3 +-- 10 files changed, 86 insertions(+), 90 deletions(-) diff --git a/libc/bionic/libgen.cpp b/libc/bionic/libgen.cpp index b98f504b0..2f29d7b63 100644 --- a/libc/bionic/libgen.cpp +++ b/libc/bionic/libgen.cpp @@ -36,6 +36,9 @@ #include "private/ThreadLocalBuffer.h" +static ThreadLocalBuffer g_basename_tls_buffer; +static ThreadLocalBuffer g_dirname_tls_buffer; + __LIBC64_HIDDEN__ int basename_r(const char* path, char* buffer, size_t buffer_size) { const char* startp = NULL; const char* endp = NULL; @@ -147,17 +150,14 @@ __LIBC64_HIDDEN__ int dirname_r(const char* path, char* buffer, size_t buffer_si return result; } -GLOBAL_INIT_THREAD_LOCAL_BUFFER(basename); -GLOBAL_INIT_THREAD_LOCAL_BUFFER(dirname); - char* basename(const char* path) { - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, basename, MAXPATHLEN); - int rc = basename_r(path, basename_tls_buffer, basename_tls_buffer_size); - return (rc < 0) ? NULL : basename_tls_buffer; + char* buf = g_basename_tls_buffer.get(); + int rc = basename_r(path, buf, g_basename_tls_buffer.size()); + return (rc < 0) ? NULL : buf; } char* dirname(const char* path) { - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, dirname, MAXPATHLEN); - int rc = dirname_r(path, dirname_tls_buffer, dirname_tls_buffer_size); - return (rc < 0) ? NULL : dirname_tls_buffer; + char* buf = g_dirname_tls_buffer.get(); + int rc = dirname_r(path, buf, g_dirname_tls_buffer.size()); + return (rc < 0) ? NULL : buf; } diff --git a/libc/bionic/mntent.cpp b/libc/bionic/mntent.cpp index 4afacda60..d169e2930 100644 --- a/libc/bionic/mntent.cpp +++ b/libc/bionic/mntent.cpp @@ -31,14 +31,13 @@ #include "private/ThreadLocalBuffer.h" -GLOBAL_INIT_THREAD_LOCAL_BUFFER(getmntent_mntent); -GLOBAL_INIT_THREAD_LOCAL_BUFFER(getmntent_strings); +static ThreadLocalBuffer g_getmntent_mntent_tls_buffer; +static ThreadLocalBuffer g_getmntent_strings_tls_buffer; mntent* getmntent(FILE* fp) { - LOCAL_INIT_THREAD_LOCAL_BUFFER(mntent*, getmntent_mntent, sizeof(mntent)); - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, getmntent_strings, BUFSIZ); - return getmntent_r(fp, getmntent_mntent_tls_buffer, - getmntent_strings_tls_buffer, getmntent_strings_tls_buffer_size); + return getmntent_r(fp, g_getmntent_mntent_tls_buffer.get(), + g_getmntent_strings_tls_buffer.get(), + g_getmntent_strings_tls_buffer.size()); } mntent* getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len) { diff --git a/libc/bionic/pty.cpp b/libc/bionic/pty.cpp index 884714799..1a3784753 100644 --- a/libc/bionic/pty.cpp +++ b/libc/bionic/pty.cpp @@ -38,8 +38,8 @@ #include "private/ThreadLocalBuffer.h" -GLOBAL_INIT_THREAD_LOCAL_BUFFER(ptsname); -GLOBAL_INIT_THREAD_LOCAL_BUFFER(ttyname); +static ThreadLocalBuffer g_ptsname_tls_buffer; +static ThreadLocalBuffer g_ttyname_tls_buffer; int getpt() { return posix_openpt(O_RDWR|O_NOCTTY); @@ -54,9 +54,9 @@ int posix_openpt(int flags) { } char* ptsname(int fd) { - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, ptsname, 32); - int error = ptsname_r(fd, ptsname_tls_buffer, ptsname_tls_buffer_size); - return (error == 0) ? ptsname_tls_buffer : NULL; + char* buf = g_ptsname_tls_buffer.get(); + int error = ptsname_r(fd, buf, g_ptsname_tls_buffer.size()); + return (error == 0) ? buf : NULL; } int ptsname_r(int fd, char* buf, size_t len) { @@ -80,9 +80,9 @@ int ptsname_r(int fd, char* buf, size_t len) { } char* ttyname(int fd) { - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, ttyname, 64); - int error = ttyname_r(fd, ttyname_tls_buffer, ttyname_tls_buffer_size); - return (error == 0) ? ttyname_tls_buffer : NULL; + char* buf = g_ttyname_tls_buffer.get(); + int error = ttyname_r(fd, buf, g_ttyname_tls_buffer.size()); + return (error == 0) ? buf : NULL; } int ttyname_r(int fd, char* buf, size_t len) { diff --git a/libc/bionic/strerror.cpp b/libc/bionic/strerror.cpp index d1518ffc4..f74194fd9 100644 --- a/libc/bionic/strerror.cpp +++ b/libc/bionic/strerror.cpp @@ -31,16 +31,16 @@ extern "C" const char* __strerror_lookup(int); -GLOBAL_INIT_THREAD_LOCAL_BUFFER(strerror); +static ThreadLocalBuffer g_strerror_tls_buffer; char* strerror(int error_number) { // Just return the original constant in the easy cases. char* result = const_cast(__strerror_lookup(error_number)); - if (result != NULL) { + if (result != nullptr) { return result; } - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strerror, NL_TEXTMAX); - strerror_r(error_number, strerror_tls_buffer, strerror_tls_buffer_size); - return strerror_tls_buffer; + result = g_strerror_tls_buffer.get(); + strerror_r(error_number, result, g_strerror_tls_buffer.size()); + return result; } diff --git a/libc/bionic/strsignal.cpp b/libc/bionic/strsignal.cpp index 9f0193acf..c389ddd3b 100644 --- a/libc/bionic/strsignal.cpp +++ b/libc/bionic/strsignal.cpp @@ -32,7 +32,7 @@ extern "C" const char* __strsignal_lookup(int); extern "C" const char* __strsignal(int, char*, size_t); -GLOBAL_INIT_THREAD_LOCAL_BUFFER(strsignal); +static ThreadLocalBuffer g_strsignal_tls_buffer; char* strsignal(int signal_number) { // Just return the original constant in the easy cases. @@ -41,6 +41,6 @@ char* strsignal(int signal_number) { return result; } - LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strsignal, NL_TEXTMAX); - return const_cast(__strsignal(signal_number, strsignal_tls_buffer, strsignal_tls_buffer_size)); + return const_cast(__strsignal(signal_number, g_strsignal_tls_buffer.get(), + g_strsignal_tls_buffer.size())); } diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index f9a31b9af..d4440c434 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -49,25 +49,12 @@ // functions to share state, but functions can't clobber // functions' state and vice versa. -GLOBAL_INIT_THREAD_LOCAL_BUFFER(group); - struct group_state_t { group group_; char* group_members_[2]; char group_name_buffer_[32]; }; -static group_state_t* __group_state() { - LOCAL_INIT_THREAD_LOCAL_BUFFER(group_state_t*, group, sizeof(group_state_t)); - if (group_tls_buffer != NULL) { - memset(group_tls_buffer, 0, sizeof(group_state_t)); - group_tls_buffer->group_.gr_mem = group_tls_buffer->group_members_; - } - return group_tls_buffer; -} - -GLOBAL_INIT_THREAD_LOCAL_BUFFER(passwd); - struct passwd_state_t { passwd passwd_; char name_buffer_[32]; @@ -75,9 +62,16 @@ struct passwd_state_t { char sh_buffer_[32]; }; -static passwd_state_t* __passwd_state() { - LOCAL_INIT_THREAD_LOCAL_BUFFER(passwd_state_t*, passwd, sizeof(passwd_state_t)); - return passwd_tls_buffer; +static ThreadLocalBuffer g_group_tls_buffer; +static ThreadLocalBuffer g_passwd_tls_buffer; + +static group_state_t* __group_state() { + group_state_t* result = g_group_tls_buffer.get(); + if (result != nullptr) { + memset(result, 0, sizeof(group_state_t)); + result->group_.gr_mem = result->group_members_; + } + return result; } static int do_getpw_r(int by_name, const char* name, uid_t uid, @@ -361,7 +355,7 @@ static group* app_id_to_group(gid_t gid, group_state_t* state) { } passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. - passwd_state_t* state = __passwd_state(); + passwd_state_t* state = g_passwd_tls_buffer.get(); if (state == NULL) { return NULL; } @@ -374,7 +368,7 @@ passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. } passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. - passwd_state_t* state = __passwd_state(); + passwd_state_t* state = g_passwd_tls_buffer.get(); if (state == NULL) { return NULL; } diff --git a/libc/dns/resolv/res_state.c b/libc/dns/resolv/res_state.c index 459f07394..afccd9979 100644 --- a/libc/dns/resolv/res_state.c +++ b/libc/dns/resolv/res_state.c @@ -39,8 +39,6 @@ #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include -#include "private/ThreadLocalBuffer.h" - /* Set to 1 to enable debug traces */ #define DEBUG 0 @@ -105,7 +103,11 @@ _res_thread_free( void* _rt ) free(rt); } -BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(_res_key, _res_thread_free); +static pthread_key_t _res_key; + +__attribute__((constructor)) static void __res_key_init() { + pthread_key_create(&_res_key, _res_thread_free); +} static _res_thread* _res_thread_get(void) diff --git a/libc/private/ThreadLocalBuffer.h b/libc/private/ThreadLocalBuffer.h index cc4731703..5e436659a 100644 --- a/libc/private/ThreadLocalBuffer.h +++ b/libc/private/ThreadLocalBuffer.h @@ -32,32 +32,30 @@ #include #include -// libstdc++ currently contains __cxa_guard_acquire and __cxa_guard_release, -// so we make do with macros instead of a C++ class. -// TODO: move __cxa_guard_acquire and __cxa_guard_release into libc. +// TODO: use __thread instead? -// We used to use pthread_once to initialize the keys, but life is more predictable -// if we allocate them all up front when the C library starts up, via __constructor__. -#define BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(key_name, key_destructor) \ - static pthread_key_t key_name; \ - __attribute__((constructor)) static void __bionic_tls_ ## key_name ## _key_init() { \ - pthread_key_create(&key_name, key_destructor); \ +template +class ThreadLocalBuffer { + public: + ThreadLocalBuffer() { + // We used to use pthread_once to initialize the keys, but life is more predictable + // if we allocate them all up front when the C library starts up, via __constructor__. + pthread_key_create(&key_, free); } -#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \ - static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \ - free(buffer); \ - } \ - BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR(__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy) + T* get() { + T* result = reinterpret_cast(pthread_getspecific(key_)); + if (result == nullptr) { + result = reinterpret_cast(calloc(1, Size)); + pthread_setspecific(key_, result); + } + return result; + } -// Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized. -#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \ - type name ## _tls_buffer = \ - reinterpret_cast(pthread_getspecific(__bionic_tls_ ## name ## _key)); \ - if (name ## _tls_buffer == NULL) { \ - name ## _tls_buffer = reinterpret_cast(calloc(1, byte_count)); \ - pthread_setspecific(__bionic_tls_ ## name ## _key, name ## _tls_buffer); \ - } \ - const size_t name ## _tls_buffer_size __attribute__((unused)) = byte_count + size_t size() { return Size; } + + private: + pthread_key_t key_; +}; #endif // _BIONIC_THREAD_LOCAL_BUFFER_H_included diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h index 1ab8d4a26..414d1711d 100644 --- a/libc/private/bionic_tls.h +++ b/libc/private/bionic_tls.h @@ -72,22 +72,26 @@ enum { /* * Bionic uses some pthread keys internally. All pthread keys used internally - * should be created in constructors, except for keys that may be used in or before constructors. + * should be created in constructors, except for keys that may be used in or + * before constructors. + * * We need to manually maintain the count of pthread keys used internally, but * pthread_test should fail if we forget. - * Following are current pthread keys used internally by libc: - * basename libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * dirname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) + * + * These are the pthread keys currently used internally by libc: + * + * basename libc (ThreadLocalBuffer) + * dirname libc (ThreadLocalBuffer) * uselocale libc (can be used in constructors) - * getmntent_mntent libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * getmntent_strings libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * ptsname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * ttyname libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * strerror libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * strsignal libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * passwd libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * group libc (GLOBAL_INIT_THREAD_LOCAL_BUFFER) - * _res_key libc (BIONIC_PTHREAD_KEY_WITH_CONSTRUCTOR) + * getmntent_mntent libc (ThreadLocalBuffer) + * getmntent_strings libc (ThreadLocalBuffer) + * ptsname libc (ThreadLocalBuffer) + * ttyname libc (ThreadLocalBuffer) + * strerror libc (ThreadLocalBuffer) + * strsignal libc (ThreadLocalBuffer) + * passwd libc (ThreadLocalBuffer) + * group libc (ThreadLocalBuffer) + * _res_key libc (constructor in BSD code) */ #define LIBC_PTHREAD_KEY_RESERVED_COUNT 12 diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 13d743f87..16bf9c0cb 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -68,8 +68,7 @@ TEST(pthread, pthread_key_many_distinct) { for (int i = 0; i < nkeys; ++i) { pthread_key_t key; - // If this fails, it's likely that GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT is - // wrong. + // If this fails, it's likely that LIBC_PTHREAD_KEY_RESERVED_COUNT is wrong. ASSERT_EQ(0, pthread_key_create(&key, NULL)) << i << " of " << nkeys; keys.push_back(key); ASSERT_EQ(0, pthread_setspecific(key, reinterpret_cast(i))); From 20d89cb5b0d5eb7546a8fe8da44bbd91564dbdda Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 30 Mar 2015 18:43:38 -0700 Subject: [PATCH 045/123] Fix long lines and replace macros with functions. Change-Id: I4e1cab488d5b2c8e4289da617350a86e72a4ba12 --- linker/debugger.cpp | 2 +- linker/linker.cpp | 140 +++++++++++++++++++------------- linker/linker.h | 6 +- linker/linker_mips.cpp | 7 +- linker/linker_phdr.cpp | 29 ++++--- linker/linker_phdr.h | 17 ++-- linker/linker_reloc_iterators.h | 45 ++++++---- 7 files changed, 153 insertions(+), 93 deletions(-) diff --git a/linker/debugger.cpp b/linker/debugger.cpp index 6fe9524e7..357fbdc99 100644 --- a/linker/debugger.cpp +++ b/linker/debugger.cpp @@ -221,7 +221,7 @@ static void send_debuggerd_packet(siginfo_t* info) { if (ret != 0) { if (ret == EBUSY) { __libc_format_log(ANDROID_LOG_INFO, "libc", - "Another thread has contacted debuggerd first, stop and wait for process to die."); + "Another thread contacted debuggerd first; not contacting debuggerd."); // This will never complete since the lock is never released. pthread_mutex_lock(&crash_mutex); } else { diff --git a/linker/linker.cpp b/linker/linker.cpp index 8e8ba841d..ebf125e1f 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -140,10 +140,13 @@ size_t linker_get_error_buffer_size() { // This function is an empty stub where GDB locates a breakpoint to get notified // about linker activity. -extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(); +extern "C" +void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(); static pthread_mutex_t g__r_debug_mutex = PTHREAD_MUTEX_INITIALIZER; -static r_debug _r_debug = {1, nullptr, reinterpret_cast(&rtld_db_dlactivity), r_debug::RT_CONSISTENT, 0}; +static r_debug _r_debug = + {1, nullptr, reinterpret_cast(&rtld_db_dlactivity), r_debug::RT_CONSISTENT, 0}; + static link_map* r_debug_tail = 0; static void insert_soinfo_into_debug_map(soinfo* info) { @@ -233,7 +236,8 @@ void SoinfoListAllocator::free(LinkedListEntry* entry) { g_soinfo_links_allocator.free(entry); } -static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags) { +static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, + off64_t file_offset, uint32_t rtld_flags) { if (strlen(name) >= SOINFO_NAME_LEN) { DL_ERR("library name \"%s\" too long", name); return nullptr; @@ -434,7 +438,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { ElfW(Sym)* s = symtab_ + n; - if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { + if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + is_symbol_global_and_defined(this, s)) { TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", symbol_name.get_name(), name, reinterpret_cast(s->st_value), static_cast(s->st_size)); @@ -448,7 +453,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { return nullptr; } -soinfo::soinfo(const char* name, const struct stat* file_stat, off64_t file_offset, int rtld_flags) { +soinfo::soinfo(const char* name, const struct stat* file_stat, + off64_t file_offset, int rtld_flags) { memset(this, 0, sizeof(*this)); strlcpy(this->name, name, sizeof(this->name)); @@ -1002,7 +1008,8 @@ static soinfo* load_library(LoadTaskList& load_tasks, return nullptr; } if (file_offset >= file_stat.st_size) { - DL_ERR("file offset for the library \"%s\" >= file size: %" PRId64 " >= %" PRId64, name, file_offset, file_stat.st_size); + DL_ERR("file offset for the library \"%s\" >= file size: %" PRId64 " >= %" PRId64, + name, file_offset, file_stat.st_size); return nullptr; } @@ -1014,7 +1021,8 @@ static soinfo* load_library(LoadTaskList& load_tasks, si->get_st_dev() == file_stat.st_dev && si->get_st_ino() == file_stat.st_ino && si->get_file_offset() == file_offset) { - TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name); + TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " + "will return existing soinfo", name, si->name); return si; } } @@ -1067,7 +1075,8 @@ static soinfo *find_loaded_library_by_soname(const char* name) { return nullptr; } -static soinfo* find_library_internal(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) { +static soinfo* find_library_internal(LoadTaskList& load_tasks, const char* name, + int rtld_flags, const android_dlextinfo* extinfo) { soinfo* si = find_loaded_library_by_soname(name); // Library might still be loaded, the accurate detection @@ -1141,7 +1150,8 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[] }); // Step 1: load and pre-link all DT_NEEDED libraries in breadth first order. - for (LoadTask::unique_ptr task(load_tasks.pop_front()); task.get() != nullptr; task.reset(load_tasks.pop_front())) { + for (LoadTask::unique_ptr task(load_tasks.pop_front()); + task.get() != nullptr; task.reset(load_tasks.pop_front())) { soinfo* si = find_library_internal(load_tasks, task->get_name(), rtld_flags, extinfo); if (si == nullptr) { return false; @@ -1320,8 +1330,8 @@ void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { // snprintf again. size_t required_len = strlen(kDefaultLdPaths[0]) + strlen(kDefaultLdPaths[1]) + 2; if (buffer_size < required_len) { - __libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: buffer len %zu, required len %zu", - buffer_size, required_len); + __libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: " + "buffer len %zu, required len %zu", buffer_size, required_len); } char* end = stpcpy(buffer, kDefaultLdPaths[0]); *end = ':'; @@ -1344,7 +1354,8 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) } if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) { - DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags); + DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without " + "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags); return nullptr; } } @@ -1366,7 +1377,8 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { typedef ElfW(Addr) (*ifunc_resolver_t)(void); ifunc_resolver_t ifunc_resolver = reinterpret_cast(resolver_addr); ElfW(Addr) ifunc_addr = ifunc_resolver(); - TRACE_TYPE(RELO, "Called ifunc_resolver@%p. The result is %p", ifunc_resolver, reinterpret_cast(ifunc_addr)); + TRACE_TYPE(RELO, "Called ifunc_resolver@%p. The result is %p", + ifunc_resolver, reinterpret_cast(ifunc_addr)); return ifunc_addr; } @@ -1378,7 +1390,8 @@ static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { } #else static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { - if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) { + if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || + ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) { return *reinterpret_cast(reloc_addr); } return 0; @@ -1386,7 +1399,8 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { #endif template -bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { +bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, + const soinfo_list_t& local_group) { for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); if (rel == nullptr) { @@ -1523,15 +1537,18 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO ABS32 %16llx <- %16llx %s\n", reloc, (sym_addr + addend), sym_name); - if ((static_cast(INT32_MIN) <= (*reinterpret_cast(reloc) + (sym_addr + addend))) && - ((*reinterpret_cast(reloc) + (sym_addr + addend)) <= static_cast(UINT32_MAX))) { - *reinterpret_cast(reloc) += (sym_addr + addend); - } else { - DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", - (*reinterpret_cast(reloc) + (sym_addr + addend)), - static_cast(INT32_MIN), - static_cast(UINT32_MAX)); - return false; + { + const ElfW(Addr) reloc_value = *reinterpret_cast(reloc); + const ElfW(Addr) min_value = static_cast(INT32_MIN); + const ElfW(Addr) max_value = static_cast(UINT32_MAX); + if ((min_value <= (reloc_value + (sym_addr + addend))) && + ((reloc_value + (sym_addr + addend)) <= max_value)) { + *reinterpret_cast(reloc) += (sym_addr + addend); + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + (reloc_value + (sym_addr + addend)), min_value, max_value); + return false; + } } break; case R_AARCH64_ABS16: @@ -1539,15 +1556,18 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO ABS16 %16llx <- %16llx %s\n", reloc, (sym_addr + addend), sym_name); - if ((static_cast(INT16_MIN) <= (*reinterpret_cast(reloc) + (sym_addr + addend))) && - ((*reinterpret_cast(reloc) + (sym_addr + addend)) <= static_cast(UINT16_MAX))) { - *reinterpret_cast(reloc) += (sym_addr + addend); - } else { - DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", - (*reinterpret_cast(reloc) + (sym_addr + addend)), - static_cast(INT16_MIN), - static_cast(UINT16_MAX)); - return false; + { + const ElfW(Addr) reloc_value = *reinterpret_cast(reloc); + const ElfW(Addr) min_value = static_cast(INT16_MIN); + const ElfW(Addr) max_value = static_cast(UINT16_MAX); + if ((min_value <= (reloc_value + (sym_addr + addend))) && + ((reloc_value + (sym_addr + addend)) <= max_value)) { + *reinterpret_cast(reloc) += (sym_addr + addend); + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + reloc_value + (sym_addr + addend), min_value, max_value); + return false; + } } break; case R_AARCH64_PREL64: @@ -1562,15 +1582,18 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO REL32 %16llx <- %16llx - %16llx %s\n", reloc, (sym_addr + addend), rel->r_offset, sym_name); - if ((static_cast(INT32_MIN) <= (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset))) && - ((*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast(UINT32_MAX))) { - *reinterpret_cast(reloc) += ((sym_addr + addend) - rel->r_offset); - } else { - DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", - (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)), - static_cast(INT32_MIN), - static_cast(UINT32_MAX)); - return false; + { + const ElfW(Addr) reloc_value = *reinterpret_cast(reloc); + const ElfW(Addr) min_value = static_cast(INT32_MIN); + const ElfW(Addr) max_value = static_cast(UINT32_MAX); + if ((min_value <= (reloc_value + ((sym_addr + addend) - rel->r_offset))) && + ((reloc_value + ((sym_addr + addend) - rel->r_offset)) <= max_value)) { + *reinterpret_cast(reloc) += ((sym_addr + addend) - rel->r_offset); + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + reloc_value + ((sym_addr + addend) - rel->r_offset), min_value, max_value); + return false; + } } break; case R_AARCH64_PREL16: @@ -1578,15 +1601,18 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO REL16 %16llx <- %16llx - %16llx %s\n", reloc, (sym_addr + addend), rel->r_offset, sym_name); - if ((static_cast(INT16_MIN) <= (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset))) && - ((*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast(UINT16_MAX))) { - *reinterpret_cast(reloc) += ((sym_addr + addend) - rel->r_offset); - } else { - DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", - (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)), - static_cast(INT16_MIN), - static_cast(UINT16_MAX)); - return false; + { + const ElfW(Addr) reloc_value = *reinterpret_cast(reloc); + const ElfW(Addr) min_value = static_cast(INT16_MIN); + const ElfW(Addr) max_value = static_cast(UINT16_MAX); + if ((min_value <= (reloc_value + ((sym_addr + addend) - rel->r_offset))) && + ((reloc_value + ((sym_addr + addend) - rel->r_offset)) <= max_value)) { + *reinterpret_cast(reloc) += ((sym_addr + addend) - rel->r_offset); + } else { + DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx", + reloc_value + ((sym_addr + addend) - rel->r_offset), min_value, max_value); + return false; + } } break; @@ -1683,7 +1709,8 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa } #endif // !defined(__mips__) -void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) { +void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, + size_t count, bool reverse) { if (functions == nullptr) { return; } @@ -2056,10 +2083,12 @@ bool soinfo::prelink_image() { gnu_bloom_filter_ = reinterpret_cast(load_bias + d->d_un.d_ptr + 16); gnu_bucket_ = reinterpret_cast(gnu_bloom_filter_ + gnu_maskwords_); // amend chain for symndx = header[1] - gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - reinterpret_cast(load_bias + d->d_un.d_ptr)[1]; + gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - + reinterpret_cast(load_bias + d->d_un.d_ptr)[1]; if (!powerof2(gnu_maskwords_)) { - DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, name); + DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", + gnu_maskwords_, name); return false; } --gnu_maskwords_; @@ -2316,7 +2345,8 @@ bool soinfo::prelink_image() { case DT_MIPS_RLD_MAP2: // Set the DT_MIPS_RLD_MAP2 entry to the address of _r_debug for GDB. { - r_debug** dp = reinterpret_cast(reinterpret_cast(d) + d->d_un.d_val); + r_debug** dp = reinterpret_cast( + reinterpret_cast(d) + d->d_un.d_val); *dp = &_r_debug; } break; diff --git a/linker/linker.h b/linker/linker.h index 05735f64b..bf3e7bf5d 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -238,7 +238,8 @@ struct soinfo { void call_destructors(); void call_pre_init_constructors(); bool prelink_image(); - bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo); + bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, + const android_dlextinfo* extinfo); void add_child(soinfo* child); void remove_all_links(); @@ -289,7 +290,8 @@ struct soinfo { void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void call_function(const char* function_name, linker_function_t function); template - bool relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group); + bool relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, + const soinfo_list_t& local_group); private: // This part of the structure is only available diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index f0bde55cd..44d39fdef 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -47,7 +47,9 @@ template bool soinfo::relocate>( const soinfo_list_t& local_group); template -bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { +bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group) { for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); @@ -115,7 +117,8 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa return true; } -bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) { +bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, + const soinfo_list_t& local_group) { ElfW(Addr)** got = plt_got_; if (got == nullptr) { return true; diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 38e62627a..2c4ca15cc 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -240,7 +240,8 @@ bool ElfReader::ReadProgramHeader() { phdr_size_ = page_max - page_min; - void* mmap_result = mmap64(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, file_offset_ + page_min); + void* mmap_result = + mmap64(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, file_offset_ + page_min); if (mmap_result == MAP_FAILED) { DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno)); return false; @@ -449,7 +450,8 @@ static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_c * Return: * 0 on error, -1 on failure (error code in errno). */ -int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); } @@ -469,7 +471,8 @@ int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, * Return: * 0 on error, -1 on failure (error code in errno). */ -int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); } @@ -531,7 +534,8 @@ static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t p * Return: * 0 on error, -1 on failure (error code in errno). */ -int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { +int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ); } @@ -547,7 +551,9 @@ int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count * Return: * 0 on error, -1 on failure (error code in errno). */ -int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, +int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr) load_bias, int fd) { const ElfW(Phdr)* phdr = phdr_table; const ElfW(Phdr)* phdr_limit = phdr + phdr_count; @@ -592,7 +598,9 @@ int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_cou * Return: * 0 on error, -1 on failure (error code in errno). */ -int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, +int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr) load_bias, int fd) { // Map the file at a temporary location so we can compare its contents. struct stat file_stat; @@ -725,11 +733,12 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, ElfW(Word)* dynamic_flags) { *dynamic = nullptr; - for (const ElfW(Phdr)* phdr = phdr_table, *phdr_limit = phdr + phdr_count; phdr < phdr_limit; phdr++) { - if (phdr->p_type == PT_DYNAMIC) { - *dynamic = reinterpret_cast(load_bias + phdr->p_vaddr); + for (size_t i = 0; i(load_bias + phdr.p_vaddr); if (dynamic_flags) { - *dynamic_flags = phdr->p_flags; + *dynamic_flags = phdr.p_flags; } return; } diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 6b917b44e..50f211775 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -84,17 +84,20 @@ class ElfReader { size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); -int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias); +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias); -int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias); +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); -int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias); +int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); -int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, - int fd); +int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int fd); -int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, - int fd); +int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int fd); #if defined(__arm__) int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, diff --git a/linker/linker_reloc_iterators.h b/linker/linker_reloc_iterators.h index 5db31f9f6..f28c0e0d7 100644 --- a/linker/linker_reloc_iterators.h +++ b/linker/linker_reloc_iterators.h @@ -21,15 +21,10 @@ #include -#define RELOCATION_GROUPED_BY_INFO_FLAG 1 -#define RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG 2 -#define RELOCATION_GROUPED_BY_ADDEND_FLAG 4 -#define RELOCATION_GROUP_HAS_ADDEND_FLAG 8 - -#define RELOCATION_GROUPED_BY_INFO(flags) (((flags) & RELOCATION_GROUPED_BY_INFO_FLAG) != 0) -#define RELOCATION_GROUPED_BY_OFFSET_DELTA(flags) (((flags) & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0) -#define RELOCATION_GROUPED_BY_ADDEND(flags) (((flags) & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0) -#define RELOCATION_GROUP_HAS_ADDEND(flags) (((flags) & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0) +const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; +const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; +const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; +const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; class plain_reloc_iterator { #if defined(USE_RELA) @@ -89,18 +84,19 @@ class packed_reloc_iterator { } } - if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) { + if (is_relocation_grouped_by_offset_delta()) { reloc_.r_offset += group_r_offset_delta_; } else { reloc_.r_offset += decoder_.pop_front(); } - if (!RELOCATION_GROUPED_BY_INFO(group_flags_)) { + if (!is_relocation_grouped_by_info()) { reloc_.r_info = decoder_.pop_front(); } #if defined(USE_RELA) - if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && !RELOCATION_GROUPED_BY_ADDEND(group_flags_)) { + if (is_relocation_group_has_addend() && + !is_relocation_grouped_by_addend()) { reloc_.r_addend += decoder_.pop_front(); } #endif @@ -115,22 +111,23 @@ class packed_reloc_iterator { group_size_ = decoder_.pop_front(); group_flags_ = decoder_.pop_front(); - if (RELOCATION_GROUPED_BY_OFFSET_DELTA(group_flags_)) { + if (is_relocation_grouped_by_offset_delta()) { group_r_offset_delta_ = decoder_.pop_front(); } - if (RELOCATION_GROUPED_BY_INFO(group_flags_)) { + if (is_relocation_grouped_by_info()) { reloc_.r_info = decoder_.pop_front(); } - if (RELOCATION_GROUP_HAS_ADDEND(group_flags_) && RELOCATION_GROUPED_BY_ADDEND(group_flags_)) { + if (is_relocation_group_has_addend() && + is_relocation_grouped_by_addend()) { #if !defined(USE_RELA) // This platform does not support rela, and yet we have it encoded in android_rel section. DL_ERR("unexpected r_addend in android.rel section"); return false; #else reloc_.r_addend += decoder_.pop_front(); - } else if (!RELOCATION_GROUP_HAS_ADDEND(group_flags_)) { + } else if (!is_relocation_group_has_addend()) { reloc_.r_addend = 0; #endif } @@ -139,6 +136,22 @@ class packed_reloc_iterator { return true; } + bool is_relocation_grouped_by_info() { + return (group_flags_ & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; + } + + bool is_relocation_grouped_by_offset_delta() { + return (group_flags_ & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; + } + + bool is_relocation_grouped_by_addend() { + return (group_flags_ & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; + } + + bool is_relocation_group_has_addend() { + return (group_flags_ & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; + } + decoder_t decoder_; size_t relocation_count_; size_t group_size_; From e01d32f8022966df2c1e697b679f9efe148c12c8 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 31 Mar 2015 14:57:48 -0700 Subject: [PATCH 046/123] Upgrade kernel headers to 3.18.10. Change-Id: Iff1f1c830780585990856a5114b559c61282dcd6 --- libc/kernel/uapi/linux/can/netlink.h | 14 +++++---- libc/kernel/uapi/linux/in6.h | 35 ++++++++++++----------- libc/kernel/uapi/linux/libc-compat.h | 14 +++++---- libc/kernel/uapi/linux/target_core_user.h | 30 +++++++++---------- libc/kernel/uapi/linux/version.h | 2 +- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/libc/kernel/uapi/linux/can/netlink.h b/libc/kernel/uapi/linux/can/netlink.h index 96a90ffff..a1c31593f 100644 --- a/libc/kernel/uapi/linux/can/netlink.h +++ b/libc/kernel/uapi/linux/can/netlink.h @@ -79,33 +79,35 @@ struct can_ctrlmode { #define CAN_CTRLMODE_BERR_REPORTING 0x10 #define CAN_CTRLMODE_FD 0x20 #define CAN_CTRLMODE_PRESUME_ACK 0x40 -struct can_device_stats { +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct can_device_stats { __u32 bus_error; __u32 error_warning; __u32 error_passive; - __u32 bus_off; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 bus_off; __u32 arbitration_lost; __u32 restarts; }; -enum { /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +enum { IFLA_CAN_UNSPEC, IFLA_CAN_BITTIMING, IFLA_CAN_BITTIMING_CONST, - IFLA_CAN_CLOCK, /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + IFLA_CAN_CLOCK, IFLA_CAN_STATE, IFLA_CAN_CTRLMODE, IFLA_CAN_RESTART_MS, - IFLA_CAN_RESTART, /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + IFLA_CAN_RESTART, IFLA_CAN_BERR_COUNTER, IFLA_CAN_DATA_BITTIMING, IFLA_CAN_DATA_BITTIMING_CONST, - __IFLA_CAN_MAX /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __IFLA_CAN_MAX }; #define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) #endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/libc/kernel/uapi/linux/in6.h b/libc/kernel/uapi/linux/in6.h index 15bde3d8c..e54bc336f 100644 --- a/libc/kernel/uapi/linux/in6.h +++ b/libc/kernel/uapi/linux/in6.h @@ -128,84 +128,87 @@ struct in6_flowlabel_req { /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_TLV_JUMBO 194 #define IPV6_TLV_HAO 201 +#if __UAPI_DEF_IPV6_OPTIONS #define IPV6_ADDRFORM 1 -#define IPV6_2292PKTINFO 2 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_2292PKTINFO 2 #define IPV6_2292HOPOPTS 3 #define IPV6_2292DSTOPTS 4 #define IPV6_2292RTHDR 5 -#define IPV6_2292PKTOPTIONS 6 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_2292PKTOPTIONS 6 #define IPV6_CHECKSUM 7 #define IPV6_2292HOPLIMIT 8 #define IPV6_NEXTHOP 9 -#define IPV6_AUTHHDR 10 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_AUTHHDR 10 #define IPV6_FLOWINFO 11 #define IPV6_UNICAST_HOPS 16 #define IPV6_MULTICAST_IF 17 -#define IPV6_MULTICAST_HOPS 18 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_MULTICAST_HOPS 18 #define IPV6_MULTICAST_LOOP 19 #define IPV6_ADD_MEMBERSHIP 20 #define IPV6_DROP_MEMBERSHIP 21 -#define IPV6_ROUTER_ALERT 22 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_ROUTER_ALERT 22 #define IPV6_MTU_DISCOVER 23 #define IPV6_MTU 24 #define IPV6_RECVERR 25 -#define IPV6_V6ONLY 26 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_V6ONLY 26 #define IPV6_JOIN_ANYCAST 27 #define IPV6_LEAVE_ANYCAST 28 #define IPV6_PMTUDISC_DONT 0 -#define IPV6_PMTUDISC_WANT 1 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_PMTUDISC_WANT 1 #define IPV6_PMTUDISC_DO 2 #define IPV6_PMTUDISC_PROBE 3 #define IPV6_PMTUDISC_INTERFACE 4 -#define IPV6_PMTUDISC_OMIT 5 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_PMTUDISC_OMIT 5 #define IPV6_FLOWLABEL_MGR 32 #define IPV6_FLOWINFO_SEND 33 #define IPV6_IPSEC_POLICY 34 -#define IPV6_XFRM_POLICY 35 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IPV6_XFRM_POLICY 35 +#endif #define IPV6_RECVPKTINFO 49 #define IPV6_PKTINFO 50 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_RECVHOPLIMIT 51 #define IPV6_HOPLIMIT 52 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_RECVHOPOPTS 53 #define IPV6_HOPOPTS 54 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_RTHDRDSTOPTS 55 #define IPV6_RECVRTHDR 56 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_RTHDR 57 #define IPV6_RECVDSTOPTS 58 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_DSTOPTS 59 #define IPV6_RECVPATHMTU 60 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_PATHMTU 61 #define IPV6_DONTFRAG 62 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_RECVTCLASS 66 #define IPV6_TCLASS 67 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_AUTOFLOWLABEL 70 #define IPV6_ADDR_PREFERENCES 72 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_PREFER_SRC_TMP 0x0001 #define IPV6_PREFER_SRC_PUBLIC 0x0002 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100 #define IPV6_PREFER_SRC_COA 0x0004 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_PREFER_SRC_HOME 0x0400 #define IPV6_PREFER_SRC_CGA 0x0008 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_PREFER_SRC_NONCGA 0x0800 #define IPV6_MINHOPCOUNT 73 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_ORIGDSTADDR 74 #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define IPV6_TRANSPARENT 75 #define IPV6_UNICAST_IF 76 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif diff --git a/libc/kernel/uapi/linux/libc-compat.h b/libc/kernel/uapi/linux/libc-compat.h index 7854520b6..b66ebe260 100644 --- a/libc/kernel/uapi/linux/libc-compat.h +++ b/libc/kernel/uapi/linux/libc-compat.h @@ -32,29 +32,33 @@ #define __UAPI_DEF_IPV6_MREQ 0 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 #else #define __UAPI_DEF_IN6_ADDR 1 -#define __UAPI_DEF_IN6_ADDR_ALT 1 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define __UAPI_DEF_IN6_ADDR_ALT 1 #define __UAPI_DEF_SOCKADDR_IN6 1 #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 -#endif /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define __UAPI_DEF_IPV6_OPTIONS 1 +#endif #ifdef _SYS_XATTR_H #define __UAPI_DEF_XATTR 0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #else #define __UAPI_DEF_XATTR 1 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif #else +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define __UAPI_DEF_IN6_ADDR 1 #define __UAPI_DEF_IN6_ADDR_ALT 1 -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define __UAPI_DEF_SOCKADDR_IN6 1 #define __UAPI_DEF_IPV6_MREQ 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 #define __UAPI_DEF_XATTR 1 +#endif /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif -#endif diff --git a/libc/kernel/uapi/linux/target_core_user.h b/libc/kernel/uapi/linux/target_core_user.h index ce6d26dd4..7e0cf43ac 100644 --- a/libc/kernel/uapi/linux/target_core_user.h +++ b/libc/kernel/uapi/linux/target_core_user.h @@ -21,75 +21,71 @@ #include #include /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ -#ifndef __packed -#define __packed __attribute__((packed)) -#endif #define TCMU_VERSION "1.0" -/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define TCMU_MAILBOX_VERSION 1 #define ALIGN_SIZE 64 struct tcmu_mailbox { - __u16 version; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u16 version; __u16 flags; __u32 cmdr_off; __u32 cmdr_size; - __u32 cmd_head; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 cmd_head; __u32 cmd_tail __attribute__((__aligned__(ALIGN_SIZE))); } __packed; enum tcmu_opcode { - TCMU_OP_PAD = 0, /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TCMU_OP_PAD = 0, TCMU_OP_CMD, }; struct tcmu_cmd_entry_hdr { - __u32 len_op; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 len_op; } __packed; #define TCMU_OP_MASK 0x7 #define TCMU_SENSE_BUFFERSIZE 96 -struct tcmu_cmd_entry { /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct tcmu_cmd_entry { struct tcmu_cmd_entry_hdr hdr; uint16_t cmd_id; uint16_t __pad1; - union { /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + union { struct { uint64_t cdb_off; uint64_t iov_cnt; - struct iovec iov[0]; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct iovec iov[0]; } req; struct { uint8_t scsi_status; - uint8_t __pad1; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint8_t __pad1; uint16_t __pad2; uint32_t __pad3; char sense_buffer[TCMU_SENSE_BUFFERSIZE]; - } rsp; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + } rsp; }; } __packed; #define TCMU_OP_ALIGN_SIZE sizeof(uint64_t) -enum tcmu_genl_cmd { /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +enum tcmu_genl_cmd { TCMU_CMD_UNSPEC, TCMU_CMD_ADDED_DEVICE, TCMU_CMD_REMOVED_DEVICE, - __TCMU_CMD_MAX, /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __TCMU_CMD_MAX, }; #define TCMU_CMD_MAX (__TCMU_CMD_MAX - 1) enum tcmu_genl_attr { - TCMU_ATTR_UNSPEC, /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TCMU_ATTR_UNSPEC, TCMU_ATTR_DEVICE, TCMU_ATTR_MINOR, __TCMU_ATTR_MAX, -}; /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; #define TCMU_ATTR_MAX (__TCMU_ATTR_MAX - 1) #endif diff --git a/libc/kernel/uapi/linux/version.h b/libc/kernel/uapi/linux/version.h index cb6842ac0..a6c6a2fd0 100644 --- a/libc/kernel/uapi/linux/version.h +++ b/libc/kernel/uapi/linux/version.h @@ -16,5 +16,5 @@ *** **************************************************************************** ****************************************************************************/ -#define LINUX_VERSION_CODE 201219 +#define LINUX_VERSION_CODE 201226 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) From 56b276817690f09305b9657a10f4ae4e1d79a0da Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 31 Mar 2015 16:55:42 -0700 Subject: [PATCH 047/123] stubs missing include for string.h stubs.cpp gets string.h inherited from private/android_filesystem_config.h it should not rely on this in the future. The intent is to move fs_config function into libcutils and thus deprecate any need for string.h in this include file. Change-Id: I946ec1979ef5bbb34fbcb4a99bf2cd79280bb2a3 --- libc/bionic/stubs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index f9a31b9af..7970e7cfa 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "private/android_filesystem_config.h" From cb00add1b382d1e3045876d7e1ccbee2fdce976b Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 31 Mar 2015 17:28:24 -0700 Subject: [PATCH 048/123] Remove text-relocation support for lp32 Bug: 20013628 Change-Id: Idaf8012f00ee1304d429c3b42f9ebc6b648c55b8 --- linker/linker.cpp | 38 ++----------------------- linker/linker.h | 4 +-- linker/linker_phdr.cpp | 64 ------------------------------------------ linker/linker_phdr.h | 6 ---- 4 files changed, 4 insertions(+), 108 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index ebf125e1f..49c10a7f7 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -2297,13 +2297,8 @@ bool soinfo::prelink_image() { break; case DT_TEXTREL: -#if defined(__LP64__) - DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name); + DL_ERR("text relocations (DT_TEXTREL) found in the ELF file \"%s\"", name); return false; -#else - has_text_relocations = true; - break; -#endif case DT_SYMBOLIC: has_DT_SYMBOLIC = true; @@ -2315,12 +2310,8 @@ bool soinfo::prelink_image() { case DT_FLAGS: if (d->d_un.d_val & DF_TEXTREL) { -#if defined(__LP64__) - DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name); + DL_ERR("text relocations (DF_TEXTREL) found in the ELF file \"%s\"", name); return false; -#else - has_text_relocations = true; -#endif } if (d->d_un.d_val & DF_SYMBOLIC) { has_DT_SYMBOLIC = true; @@ -2429,20 +2420,6 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group_root_ = this; } -#if !defined(__LP64__) - if (has_text_relocations) { - // Make segments writable to allow text relocations to work properly. We will later call - // phdr_table_protect_segments() after all of them are applied and all constructors are run. - DL_WARN("%s has text relocations. This is wasting memory and prevents " - "security hardening. Please fix.", name); - if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { - DL_ERR("can't unprotect loadable segments for \"%s\": %s", - name, strerror(errno)); - return false; - } - } -#endif - if (android_relocs_ != nullptr) { // check signature if (android_relocs_size_ > 3 && @@ -2513,17 +2490,6 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& DEBUG("[ finished linking %s ]", name); -#if !defined(__LP64__) - if (has_text_relocations) { - // All relocations are done, we can protect our segments back to read-only. - if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { - DL_ERR("can't protect segments for \"%s\": %s", - name, strerror(errno)); - return false; - } - } -#endif - /* We can also turn on GNU RELRO protection */ if (phdr_table_protect_gnu_relro(phdr, phnum, load_bias) < 0) { DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", diff --git a/linker/linker.h b/linker/linker.h index bf3e7bf5d..ed2691730 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -226,8 +226,8 @@ struct soinfo { // value to get the corresponding address in the process' address space. ElfW(Addr) load_bias; -#if !defined(__LP64__) - bool has_text_relocations; +#if defined(__arm__) + bool unused4; // DO NOT USE, maintained for compatibility #endif bool has_DT_SYMBOLIC; diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 2c4ca15cc..66b28aeb3 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -412,70 +412,6 @@ bool ElfReader::LoadSegments() { return true; } -/* Used internally. Used to set the protection bits of all loaded segments - * with optional extra flags (i.e. really PROT_WRITE). Used by - * phdr_table_protect_segments and phdr_table_unprotect_segments. - */ -static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, - ElfW(Addr) load_bias, int extra_prot_flags) { - const ElfW(Phdr)* phdr = phdr_table; - const ElfW(Phdr)* phdr_limit = phdr + phdr_count; - - for (; phdr < phdr_limit; phdr++) { - if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) { - continue; - } - - ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; - ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; - - int ret = mprotect(reinterpret_cast(seg_page_start), - seg_page_end - seg_page_start, - PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); - if (ret < 0) { - return -1; - } - } - return 0; -} - -/* Restore the original protection modes for all loadable segments. - * You should only call this after phdr_table_unprotect_segments and - * applying all relocations. - * - * Input: - * phdr_table -> program header table - * phdr_count -> number of entries in tables - * load_bias -> load bias - * Return: - * 0 on error, -1 on failure (error code in errno). - */ -int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, - size_t phdr_count, ElfW(Addr) load_bias) { - return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); -} - -/* Change the protection of all loaded segments in memory to writable. - * This is useful before performing relocations. Once completed, you - * will have to call phdr_table_protect_segments to restore the original - * protection flags on all segments. - * - * Note that some writable segments can also have their content turned - * to read-only by calling phdr_table_protect_gnu_relro. This is no - * performed here. - * - * Input: - * phdr_table -> program header table - * phdr_count -> number of entries in tables - * load_bias -> load bias - * Return: - * 0 on error, -1 on failure (error code in errno). - */ -int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, - size_t phdr_count, ElfW(Addr) load_bias) { - return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); -} - /* Used internally by phdr_table_protect_gnu_relro and * phdr_table_unprotect_gnu_relro. */ diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 50f211775..cc3296b3d 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -84,12 +84,6 @@ class ElfReader { size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); -int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, - size_t phdr_count, ElfW(Addr) load_bias); - -int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, - ElfW(Addr) load_bias); - int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias); From d3d018b2b4a23f850c1f349d7c3222daa6a68c23 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 1 Apr 2015 12:16:56 -0700 Subject: [PATCH 049/123] Update README.md for running tests on the host. Change-Id: Ib360d91bffd269e7acdb20ad33a7bd85345a1475 --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2c42b3ba5..79bb72a97 100644 --- a/README.md +++ b/README.md @@ -208,19 +208,17 @@ The tests are all built from the tests/ directory. The host tests require that you have `lunch`ed either an x86 or x86_64 target. $ mma - # 64-bit tests for 64-bit targets, 32-bit otherwise. - $ mm bionic-unit-tests-run-on-host - # Only exists for 64-bit targets. $ mm bionic-unit-tests-run-on-host32 + $ mm bionic-unit-tests-run-on-host64 # For 64-bit *targets* only. ### Against glibc As a way to check that our tests do in fact test the correct behavior (and not just the behavior we think is correct), it is possible to run the tests against -the host's glibc. +the host's glibc. The executables are already in your path. $ mma - $ bionic-unit-tests-glibc32 # already in your path + $ bionic-unit-tests-glibc32 $ bionic-unit-tests-glibc64 From 0ad256c1b234cddc97290be761f8a0163a5c6c00 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 1 Apr 2015 12:22:40 -0700 Subject: [PATCH 050/123] Fix ftw tests when run as non-root. Root can create subdirectories inside non-writable directories, but other users can't. Change-Id: I102fe610d1bd2733aebf184b544e58612465287d --- tests/ftw_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ftw_test.cpp b/tests/ftw_test.cpp index 6741d003e..b7e5bd514 100644 --- a/tests/ftw_test.cpp +++ b/tests/ftw_test.cpp @@ -30,11 +30,11 @@ static void MakeTree(const char* root) { char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/dir", root); - ASSERT_EQ(0, mkdir(path, 0555)); + ASSERT_EQ(0, mkdir(path, 0755)) << path; snprintf(path, sizeof(path), "%s/dir/sub", root); - ASSERT_EQ(0, mkdir(path, 0555)); + ASSERT_EQ(0, mkdir(path, 0555)) << path; snprintf(path, sizeof(path), "%s/unreadable-dir", root); - ASSERT_EQ(0, mkdir(path, 0000)); + ASSERT_EQ(0, mkdir(path, 0000)) << path; snprintf(path, sizeof(path), "%s/dangler", root); ASSERT_EQ(0, symlink("/does-not-exist", path)); From 56be6ed9e4ac99fdd920090ee89c57e3cf55e885 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Wed, 1 Apr 2015 21:18:48 +0000 Subject: [PATCH 051/123] Revert "Remove text-relocation support for lp32" This reverts commit cb00add1b382d1e3045876d7e1ccbee2fdce976b. Bug: 20020312 Bug: 20013628 Change-Id: I8baa3d4b6c7fef50c9e2531257d5b96762099eb3 --- linker/linker.cpp | 38 +++++++++++++++++++++++-- linker/linker.h | 4 +-- linker/linker_phdr.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++ linker/linker_phdr.h | 6 ++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 49c10a7f7..ebf125e1f 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -2297,8 +2297,13 @@ bool soinfo::prelink_image() { break; case DT_TEXTREL: - DL_ERR("text relocations (DT_TEXTREL) found in the ELF file \"%s\"", name); +#if defined(__LP64__) + DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name); return false; +#else + has_text_relocations = true; + break; +#endif case DT_SYMBOLIC: has_DT_SYMBOLIC = true; @@ -2310,8 +2315,12 @@ bool soinfo::prelink_image() { case DT_FLAGS: if (d->d_un.d_val & DF_TEXTREL) { - DL_ERR("text relocations (DF_TEXTREL) found in the ELF file \"%s\"", name); +#if defined(__LP64__) + DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name); return false; +#else + has_text_relocations = true; +#endif } if (d->d_un.d_val & DF_SYMBOLIC) { has_DT_SYMBOLIC = true; @@ -2420,6 +2429,20 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group_root_ = this; } +#if !defined(__LP64__) + if (has_text_relocations) { + // Make segments writable to allow text relocations to work properly. We will later call + // phdr_table_protect_segments() after all of them are applied and all constructors are run. + DL_WARN("%s has text relocations. This is wasting memory and prevents " + "security hardening. Please fix.", name); + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + name, strerror(errno)); + return false; + } + } +#endif + if (android_relocs_ != nullptr) { // check signature if (android_relocs_size_ > 3 && @@ -2490,6 +2513,17 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& DEBUG("[ finished linking %s ]", name); +#if !defined(__LP64__) + if (has_text_relocations) { + // All relocations are done, we can protect our segments back to read-only. + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + name, strerror(errno)); + return false; + } + } +#endif + /* We can also turn on GNU RELRO protection */ if (phdr_table_protect_gnu_relro(phdr, phnum, load_bias) < 0) { DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", diff --git a/linker/linker.h b/linker/linker.h index ed2691730..bf3e7bf5d 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -226,8 +226,8 @@ struct soinfo { // value to get the corresponding address in the process' address space. ElfW(Addr) load_bias; -#if defined(__arm__) - bool unused4; // DO NOT USE, maintained for compatibility +#if !defined(__LP64__) + bool has_text_relocations; #endif bool has_DT_SYMBOLIC; diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 66b28aeb3..2c4ca15cc 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -412,6 +412,70 @@ bool ElfReader::LoadSegments() { return true; } +/* Used internally. Used to set the protection bits of all loaded segments + * with optional extra flags (i.e. really PROT_WRITE). Used by + * phdr_table_protect_segments and phdr_table_unprotect_segments. + */ +static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias, int extra_prot_flags) { + const ElfW(Phdr)* phdr = phdr_table; + const ElfW(Phdr)* phdr_limit = phdr + phdr_count; + + for (; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) { + continue; + } + + ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + int ret = mprotect(reinterpret_cast(seg_page_start), + seg_page_end - seg_page_start, + PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); + if (ret < 0) { + return -1; + } + } + return 0; +} + +/* Restore the original protection modes for all loadable segments. + * You should only call this after phdr_table_unprotect_segments and + * applying all relocations. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { + return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); +} + +/* Change the protection of all loaded segments in memory to writable. + * This is useful before performing relocations. Once completed, you + * will have to call phdr_table_protect_segments to restore the original + * protection flags on all segments. + * + * Note that some writable segments can also have their content turned + * to read-only by calling phdr_table_protect_gnu_relro. This is no + * performed here. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias) { + return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); +} + /* Used internally by phdr_table_protect_gnu_relro and * phdr_table_unprotect_gnu_relro. */ diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index cc3296b3d..50f211775 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -84,6 +84,12 @@ class ElfReader { size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); +int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, + size_t phdr_count, ElfW(Addr) load_bias); + +int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, + ElfW(Addr) load_bias); + int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias); From d6bf3d5e19f192dc1949b573d931d487c42730fa Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Wed, 1 Apr 2015 16:26:54 -0700 Subject: [PATCH 052/123] Don't export new/delete from libc shared libraries. This used to be handled by -fvisibility=hidden on libc_cxa, but that was broken by the resolution of https://llvm.org/PR22419 (introduced to Android in today's clang update). Now we just use a version script that prevents these from being re-exported from our shared libraries. Change-Id: Ib290e1d0d7426e09ad17a91178162fff6dbdcfa9 --- libc/Android.mk | 27 +++++++++++++++++++++------ libc/version_script.txt | 12 ++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 libc/version_script.txt diff --git a/libc/Android.mk b/libc/Android.mk index e438552eb..0de0fb21c 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1347,9 +1347,6 @@ LOCAL_CFLAGS := $(libc_common_cflags) LOCAL_CONLYFLAGS := $(libc_common_conlyflags) LOCAL_CPPFLAGS := $(libc_common_cppflags) -# TODO: This is to work around b/19059885. Remove after root cause is fixed -LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv - LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ $(libc_arch_dynamic_src_files) \ @@ -1359,8 +1356,10 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libc LOCAL_CLANG := $(use_clang) -LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) LOCAL_REQUIRED_MODULES := tzdata +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(libc_common_additional_dependencies) \ + $(LOCAL_PATH)/version_script.txt \ # Leave the symbols in the shared library so that stack unwinders can produce # meaningful name resolution. @@ -1379,11 +1378,17 @@ LOCAL_WHOLE_STATIC_LIBRARIES := libc_common LOCAL_CXX_STL := none LOCAL_SYSTEM_SHARED_LIBRARIES := +# Don't re-export new/delete and friends, even if the compiler really wants to. +LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt + # We'd really like to do this for all architectures, but since this wasn't done # before, these symbols must continue to be exported on LP32 for binary # compatibility. LOCAL_LDFLAGS_64 := -Wl,--exclude-libs,libgcc.a +# TODO: This is to work around b/19059885. Remove after root cause is fixed +LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv + $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags)) $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files)) # special for arm @@ -1431,7 +1436,9 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libc_malloc_debug_leak LOCAL_CLANG := $(use_clang) -LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(libc_common_additional_dependencies) \ + $(LOCAL_PATH)/version_script.txt \ LOCAL_SHARED_LIBRARIES := libc libdl LOCAL_CXX_STL := none @@ -1442,6 +1449,9 @@ LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm LOCAL_STATIC_LIBRARIES += libc++abi LOCAL_ALLOW_UNDEFINED_SYMBOLS := true +# Don't re-export new/delete and friends, even if the compiler really wants to. +LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt + # Don't install on release build LOCAL_MODULE_TAGS := eng debug LOCAL_ADDRESS_SANITIZER := false @@ -1471,12 +1481,17 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libc_malloc_debug_qemu LOCAL_CLANG := $(use_clang) -LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(libc_common_additional_dependencies) \ + $(LOCAL_PATH)/version_script.txt \ LOCAL_SHARED_LIBRARIES := libc libdl LOCAL_CXX_STL := none LOCAL_SYSTEM_SHARED_LIBRARIES := +# Don't re-export new/delete and friends, even if the compiler really wants to. +LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt + # Don't install on release build LOCAL_MODULE_TAGS := eng debug LOCAL_ADDRESS_SANITIZER := false diff --git a/libc/version_script.txt b/libc/version_script.txt new file mode 100644 index 000000000..afc5e5c47 --- /dev/null +++ b/libc/version_script.txt @@ -0,0 +1,12 @@ +LIBC { + local: + _ZSt7nothrow; + _ZdaPv; + _ZdaPvRKSt9nothrow_t; + _ZdlPv; + _ZdlPvRKSt9nothrow_t; + _Znaj; + _ZnajRKSt9nothrow_t; + _Znwj; + _ZnwjRKSt9nothrow_t; +}; From 8fdb3419a51ffeda64f9c811f22a42edf9c7f633 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 1 Apr 2015 16:57:50 -0700 Subject: [PATCH 053/123] linker: never mark pages simultaneously writable / executable When the Android dynamic linker handles a text relocation, it first relaxes the permissions on the segment being modified, performs the modifications, and then restores the page permissions. The relaxation worked by adding PROT_WRITE to whatever protection bits were set in the section. In effect, the pages were getting set to PROT_READ|PROT_WRITE|PROT_EXEC, modified, then restored to PROT_READ|PROT_EXEC The SELinux kernel code differentiates between 4 different kinds of executable memory: * Executable stack (execstack) * Executable heap (execheap) * File-based executable code which has been modified (execmod) * All other executable memory (execmem) The execmod capability is only triggered by the kernel when a dirty but non-executable mmap()ed page becomes executable. When that occurs, an SELinux policy check is done to see if the execmod capability is provided by policy. However, if the page is already executable, and PROT_WRITE is added to the page, it's considered an execmem permission check, not an execmod permission check. There are certain circumstances where we may want to distinguish between execmod and execmem. This change adjusts the dynamic linker to avoid using RWX pages, so that an RX -> RW -> RX transition will properly be detected as an execmod permission check instead of an execmem permission check. Bug: 20013628 Change-Id: I14d7be29170b156942f9809023f3b2fc1f37846c --- linker/linker_phdr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 2c4ca15cc..638c9d6ba 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -429,9 +429,15 @@ static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_c ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + int prot = PFLAGS_TO_PROT(phdr->p_flags); + if ((extra_prot_flags & PROT_WRITE) != 0) { + // make sure we're never simultaneously writable / executable + prot &= ~PROT_EXEC; + } + int ret = mprotect(reinterpret_cast(seg_page_start), seg_page_end - seg_page_start, - PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); + prot | extra_prot_flags); if (ret < 0) { return -1; } From e69c24543db577d8b219ab74b0ba7566e0f13b38 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 13 Feb 2015 16:21:25 -0800 Subject: [PATCH 054/123] Refactor pthread_mutex to support 32-bit owner_tid on 64-bit devices. Bug: 19216648 Change-Id: I765ecacc9036659c766f5d1f6600e1a65364199b --- libc/bionic/pthread_mutex.cpp | 296 ++++++++++++++++------------------ tests/pthread_test.cpp | 7 +- 2 files changed, 146 insertions(+), 157 deletions(-) diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index d2ff1aeb0..5bdc5ed98 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -44,14 +44,85 @@ #include "private/bionic_time_conversions.h" #include "private/bionic_tls.h" -/* a mutex is implemented as a 32-bit integer holding the following fields +/* a mutex attribute holds the following fields + * + * bits: name description + * 0-3 type type of mutex + * 4 shared process-shared flag + */ +#define MUTEXATTR_TYPE_MASK 0x000f +#define MUTEXATTR_SHARED_MASK 0x0010 + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + *attr = PTHREAD_MUTEX_DEFAULT; + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + *attr = -1; + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type_p) +{ + int type = (*attr & MUTEXATTR_TYPE_MASK); + + if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK) { + return EINVAL; + } + + *type_p = type; + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK ) { + return EINVAL; + } + + *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type; + return 0; +} + +/* process-shared mutexes are not supported at the moment */ + +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) +{ + switch (pshared) { + case PTHREAD_PROCESS_PRIVATE: + *attr &= ~MUTEXATTR_SHARED_MASK; + return 0; + + case PTHREAD_PROCESS_SHARED: + /* our current implementation of pthread actually supports shared + * mutexes but won't cleanup if a process dies with the mutex held. + * Nevertheless, it's better than nothing. Shared mutexes are used + * by surfaceflinger and audioflinger. + */ + *attr |= MUTEXATTR_SHARED_MASK; + return 0; + } + return EINVAL; +} + +int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared) { + *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE; + return 0; +} + +/* a mutex contains a state value and a owner_tid. + * The value is implemented as a 16-bit integer holding the following fields: * * bits: name description - * 31-16 tid owner thread's tid (recursive and errorcheck only) * 15-14 type mutex type * 13 shared process-shared flag * 12-2 counter counter of recursive mutexes * 1-0 state lock state (0, 1 or 2) + * + * The owner_tid is used only in recursive and errorcheck mutex to hold the mutex owner thread tid. */ /* Convenience macro, creates a mask of 'bits' bits that starts from @@ -68,6 +139,12 @@ /* And this one does the opposite, i.e. extract a field's value from a bit pattern */ #define FIELD_FROM_BITS(val,shift,bits) (((val) >> (shift)) & ((1 << (bits))-1)) + +/* Convenience macros. + * + * These are used to form or modify the bit pattern of a given mutex value + */ + /* Mutex state: * * 0 for unlocked @@ -135,102 +212,16 @@ #define MUTEX_TYPE_BITS_RECURSIVE MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_RECURSIVE) #define MUTEX_TYPE_BITS_ERRORCHECK MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_ERRORCHECK) -/* Mutex owner field: - * - * This is only used for recursive and errorcheck mutexes. It holds the - * tid of the owning thread. We use 16 bits to represent tid here, - * so the highest tid is 65535. There is a test to check /proc/sys/kernel/pid_max - * to make sure it will not exceed our limit. - */ -#define MUTEX_OWNER_SHIFT 16 -#define MUTEX_OWNER_LEN 16 - -#define MUTEX_OWNER_FROM_BITS(v) FIELD_FROM_BITS(v,MUTEX_OWNER_SHIFT,MUTEX_OWNER_LEN) -#define MUTEX_OWNER_TO_BITS(v) FIELD_TO_BITS(v,MUTEX_OWNER_SHIFT,MUTEX_OWNER_LEN) - -/* Convenience macros. - * - * These are used to form or modify the bit pattern of a given mutex value - */ - - - -/* a mutex attribute holds the following fields - * - * bits: name description - * 0-3 type type of mutex - * 4 shared process-shared flag - */ -#define MUTEXATTR_TYPE_MASK 0x000f -#define MUTEXATTR_SHARED_MASK 0x0010 - - -int pthread_mutexattr_init(pthread_mutexattr_t *attr) -{ - *attr = PTHREAD_MUTEX_DEFAULT; - return 0; -} - -int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) -{ - *attr = -1; - return 0; -} - -int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type_p) -{ - int type = (*attr & MUTEXATTR_TYPE_MASK); - - if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK) { - return EINVAL; - } - - *type_p = type; - return 0; -} - -int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) -{ - if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK ) { - return EINVAL; - } - - *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type; - return 0; -} - -/* process-shared mutexes are not supported at the moment */ - -int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) -{ - switch (pshared) { - case PTHREAD_PROCESS_PRIVATE: - *attr &= ~MUTEXATTR_SHARED_MASK; - return 0; - - case PTHREAD_PROCESS_SHARED: - /* our current implementation of pthread actually supports shared - * mutexes but won't cleanup if a process dies with the mutex held. - * Nevertheless, it's better than nothing. Shared mutexes are used - * by surfaceflinger and audioflinger. - */ - *attr |= MUTEXATTR_SHARED_MASK; - return 0; - } - return EINVAL; -} - -int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared) { - *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE; - return 0; -} - struct pthread_mutex_internal_t { - atomic_int state; + _Atomic(uint16_t) state; #if defined(__LP64__) - char __reserved[36]; + uint16_t __pad; + atomic_int owner_tid; + char __reserved[32]; +#else + _Atomic(uint16_t) owner_tid; #endif -}; +} __attribute__((aligned(4))); static_assert(sizeof(pthread_mutex_t) == sizeof(pthread_mutex_internal_t), "pthread_mutex_t should actually be pthread_mutex_internal_t in implementation."); @@ -254,35 +245,36 @@ int pthread_mutex_init(pthread_mutex_t* mutex_interface, const pthread_mutexattr return 0; } - int state = 0; + uint16_t state = 0; if ((*attr & MUTEXATTR_SHARED_MASK) != 0) { state |= MUTEX_SHARED_MASK; } switch (*attr & MUTEXATTR_TYPE_MASK) { case PTHREAD_MUTEX_NORMAL: - state |= MUTEX_TYPE_BITS_NORMAL; - break; + state |= MUTEX_TYPE_BITS_NORMAL; + break; case PTHREAD_MUTEX_RECURSIVE: - state |= MUTEX_TYPE_BITS_RECURSIVE; - break; + state |= MUTEX_TYPE_BITS_RECURSIVE; + break; case PTHREAD_MUTEX_ERRORCHECK: - state |= MUTEX_TYPE_BITS_ERRORCHECK; - break; + state |= MUTEX_TYPE_BITS_ERRORCHECK; + break; default: return EINVAL; } atomic_init(&mutex->state, state); + atomic_init(&mutex->owner_tid, 0); return 0; } static inline __always_inline int __pthread_normal_mutex_trylock(pthread_mutex_internal_t* mutex, - int shared) { - const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + uint16_t shared) { + const uint16_t unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; + const uint16_t locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - int old_state = unlocked; + uint16_t old_state = unlocked; if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, locked_uncontended, memory_order_acquire, memory_order_relaxed))) { return 0; @@ -303,7 +295,7 @@ static inline __always_inline int __pthread_normal_mutex_trylock(pthread_mutex_i * the lock state field. */ static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex, - int shared, + uint16_t shared, const timespec* abs_timeout_or_null, clockid_t clock) { if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { @@ -312,8 +304,8 @@ static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_inte ScopedTrace trace("Contending for pthread mutex"); - const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; + const uint16_t unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; + const uint16_t locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; // We want to go to sleep until the mutex is available, which requires // promoting it to locked_contended. We need to swap in the new state @@ -341,13 +333,13 @@ static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_inte } /* - * Release a mutex of type NORMAL. The caller is responsible for determining + * Release a normal mutex. The caller is responsible for determining * that we are in fact the owner of this lock. */ static inline __always_inline void __pthread_normal_mutex_unlock(pthread_mutex_internal_t* mutex, - int shared) { - const int unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; + uint16_t shared) { + const uint16_t unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; + const uint16_t locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; // We use an atomic_exchange to release the lock. If locked_contended state // is returned, some threads is waiting for the lock and we need to wake up @@ -385,7 +377,7 @@ static inline __always_inline void __pthread_normal_mutex_unlock(pthread_mutex_i * */ static inline __always_inline int __recursive_increment(pthread_mutex_internal_t* mutex, - int old_state) { + uint16_t old_state) { // Detect recursive lock overflow and return EAGAIN. // This is safe because only the owner thread can modify the // counter bits in the mutex value. @@ -393,22 +385,18 @@ static inline __always_inline int __recursive_increment(pthread_mutex_internal_t return EAGAIN; } - // We own the mutex, but other threads are able to change the lower bits - // (e.g. promoting it to "contended"), so we need to use an atomic exchange - // loop to update the counter. The counter will not overflow in the loop, - // as only the owner thread can change it. - // The mutex is still locked, so we don't need a release fence. + // Other threads are able to change the lower bits (e.g. promoting it to "contended"), + // but the mutex counter will not overflow. So we use atomic_fetch_add operation here. + // The mutex is still locked by current thread, so we don't need a release fence. atomic_fetch_add_explicit(&mutex->state, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return 0; } static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, const timespec* abs_timeout_or_null, clockid_t clock) { - int old_state, mtype, tid, shared; - - old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); - mtype = (old_state & MUTEX_TYPE_MASK); - shared = (old_state & MUTEX_SHARED_MASK); + uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + uint16_t mtype = (old_state & MUTEX_TYPE_MASK); + uint16_t shared = (old_state & MUTEX_SHARED_MASK); // Handle common case first. if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) { @@ -416,26 +404,26 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, } // Do we already own this recursive or error-check mutex? - tid = __get_thread()->tid; - if (tid == MUTEX_OWNER_FROM_BITS(old_state)) { + pid_t tid = __get_thread()->tid; + if (tid == atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EDEADLK; } return __recursive_increment(mutex, old_state); } - const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; - const int locked_contended = mtype | shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; + const uint16_t unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + const uint16_t locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + const uint16_t locked_contended = mtype | shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; // First, if the mutex is unlocked, try to quickly acquire it. // In the optimistic case where this works, set the state to locked_uncontended. if (old_state == unlocked) { - int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, - new_state, memory_order_acquire, memory_order_relaxed))) { + locked_uncontended, memory_order_acquire, memory_order_relaxed))) { + atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return 0; } } @@ -448,13 +436,13 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, // is contention when we are in this loop. This ensures all waiters // will be unlocked. - int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_contended; // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_weak_explicit(&mutex->state, - &old_state, new_state, + &old_state, locked_contended, memory_order_acquire, memory_order_relaxed))) { + atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return 0; } continue; @@ -491,9 +479,9 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, int pthread_mutex_lock(pthread_mutex_t* mutex_interface) { pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); - int mtype = (old_state & MUTEX_TYPE_MASK); - int shared = (old_state & MUTEX_SHARED_MASK); + uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + uint16_t mtype = (old_state & MUTEX_TYPE_MASK); + uint16_t shared = (old_state & MUTEX_SHARED_MASK); // Avoid slowing down fast path of normal mutex lock operation. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { @@ -506,11 +494,9 @@ int pthread_mutex_lock(pthread_mutex_t* mutex_interface) { int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int old_state, mtype, tid, shared; - - old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); - mtype = (old_state & MUTEX_TYPE_MASK); - shared = (old_state & MUTEX_SHARED_MASK); + uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + uint16_t mtype = (old_state & MUTEX_TYPE_MASK); + uint16_t shared = (old_state & MUTEX_SHARED_MASK); // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { @@ -519,9 +505,10 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { } // Do we already own this recursive or error-check mutex? - tid = __get_thread()->tid; - if ( tid != MUTEX_OWNER_FROM_BITS(old_state) ) + pid_t tid = __get_thread()->tid; + if ( tid != atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed) ) { return EPERM; + } // If the counter is > 0, we can simply decrement it atomically. // Since other threads can mutate the lower state bits (and only the @@ -538,7 +525,8 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { // to awake. // A release fence is required to make previous stores visible to next // lock owner threads. - const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + atomic_store_explicit(&mutex->owner_tid, 0, memory_order_relaxed); + const uint16_t unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; old_state = atomic_exchange_explicit(&mutex->state, unlocked, memory_order_release); if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(old_state)) { __futex_wake_ex(&mutex->state, shared, 1); @@ -550,12 +538,12 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) { pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - int old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); - int mtype = (old_state & MUTEX_TYPE_MASK); - int shared = (old_state & MUTEX_SHARED_MASK); + uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); + uint16_t mtype = (old_state & MUTEX_TYPE_MASK); + uint16_t shared = (old_state & MUTEX_SHARED_MASK); - const int unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; - const int locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; + const uint16_t unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; + const uint16_t locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { @@ -564,7 +552,7 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) { // Do we already own this recursive or error-check mutex? pid_t tid = __get_thread()->tid; - if (tid == MUTEX_OWNER_FROM_BITS(old_state)) { + if (tid == atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EBUSY; } @@ -577,10 +565,11 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) { // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. old_state = unlocked; - int new_state = MUTEX_OWNER_TO_BITS(tid) | locked_uncontended; - if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, new_state, + if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, + locked_uncontended, memory_order_acquire, memory_order_relaxed))) { + atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return 0; } return EBUSY; @@ -617,8 +606,5 @@ int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) { if (error != 0) { return error; } - - pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface); - atomic_store_explicit(&mutex->state, 0xdead10cc, memory_order_relaxed); return 0; } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 13d743f87..7bcef51b3 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -1327,14 +1327,17 @@ TEST(pthread, pthread_mutex_RECURSIVE_wakeup) { } TEST(pthread, pthread_mutex_owner_tid_limit) { +#if defined(__BIONIC__) && !defined(__LP64__) FILE* fp = fopen("/proc/sys/kernel/pid_max", "r"); ASSERT_TRUE(fp != NULL); long pid_max; ASSERT_EQ(1, fscanf(fp, "%ld", &pid_max)); fclose(fp); - // Current pthread_mutex uses 16 bits to represent owner tid. - // Change the implementation if we need to support higher value than 65535. + // Bionic's pthread_mutex implementation on 32-bit devices uses 16 bits to represent owner tid. ASSERT_LE(pid_max, 65536); +#else + GTEST_LOG_(INFO) << "This test does nothing as 32-bit tid is supported by pthread_mutex.\n"; +#endif } class StrictAlignmentAllocator { From b0cf9288cd4c09fcad8dc8e04b18cc4d8d13bd71 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 2 Apr 2015 10:34:58 -0700 Subject: [PATCH 055/123] Remove _memset16 and _memset32 from mips/mips64 bionic. These should be in libcutils instead. Change-Id: Ibbc94755e6da61bf9ce2c8f9a047a082bb9bce24 --- libc/arch-mips/string/memset.S | 80 -------------------------------- libc/arch-mips64/string/memset.S | 80 -------------------------------- 2 files changed, 160 deletions(-) diff --git a/libc/arch-mips/string/memset.S b/libc/arch-mips/string/memset.S index 3e630caf8..09b756b77 100644 --- a/libc/arch-mips/string/memset.S +++ b/libc/arch-mips/string/memset.S @@ -67,86 +67,6 @@ #define DBG #endif -/* - * void _memset16(uint16_t* dst, uint16_t value, size_t size); - */ - -LEAF(_memset16,0) - .set noreorder -DBG /* Check parameters */ -DBG andi t0,a0,1 # a0 must be halfword aligned -DBG tne t0,zero -DBG andi t2,a2,1 # a2 must be even -DBG tne t2,zero - -#ifdef FIXARGS - # ensure count is even -#if (__mips==32) && (__mips_isa_rev>=2) - ins a2,zero,0,1 -#else - ori a2,1 - xori a2,1 -#endif -#endif - -#if (__mips==32) && (__mips_isa_rev>=2) - ins a1,a1,16,16 -#else - andi a1,0xffff - sll t3,a1,16 - or a1,t3 -#endif - - beqz a2,.Ldone - andi t1,a0,2 - beqz t1,.Lalignok - addu t0,a0,a2 # t0 is the "past the end" address - sh a1,0(a0) # store one halfword to get aligned - addu a0,2 - subu a2,2 -.Lalignok: - slti t1,a2,4 # .Laligned for 4 or more bytes - beqz t1,.Laligned - sne t1,a2,2 # one more halfword? - bnez t1,.Ldone - nop - sh a1,0(a0) -.Ldone: - j ra - nop - .set reorder -END(_memset16) - -/* - * void _memset32(uint32_t* dst, uint32_t value, size_t size); - */ - -LEAF(_memset32,0) - .set noreorder -DBG /* Check parameters */ -DBG andi t0,a0,3 # a0 must be word aligned -DBG tne t0,zero -DBG andi t2,a2,3 # a2 must be a multiple of 4 bytes -DBG tne t2,zero - -#ifdef FIXARGS - # ensure count is a multiple of 4 -#if (__mips==32) && (__mips_isa_rev>=2) - ins $a2,$0,0,2 -#else - ori a2,3 - xori a2,3 -#endif -#endif - - bnez a2,.Laligned # any work to do? - addu t0,a0,a2 # t0 is the "past the end" address - - j ra - nop - .set reorder -END(_memset32) - LEAF(memset,0) .set noreorder diff --git a/libc/arch-mips64/string/memset.S b/libc/arch-mips64/string/memset.S index 3e630caf8..09b756b77 100644 --- a/libc/arch-mips64/string/memset.S +++ b/libc/arch-mips64/string/memset.S @@ -67,86 +67,6 @@ #define DBG #endif -/* - * void _memset16(uint16_t* dst, uint16_t value, size_t size); - */ - -LEAF(_memset16,0) - .set noreorder -DBG /* Check parameters */ -DBG andi t0,a0,1 # a0 must be halfword aligned -DBG tne t0,zero -DBG andi t2,a2,1 # a2 must be even -DBG tne t2,zero - -#ifdef FIXARGS - # ensure count is even -#if (__mips==32) && (__mips_isa_rev>=2) - ins a2,zero,0,1 -#else - ori a2,1 - xori a2,1 -#endif -#endif - -#if (__mips==32) && (__mips_isa_rev>=2) - ins a1,a1,16,16 -#else - andi a1,0xffff - sll t3,a1,16 - or a1,t3 -#endif - - beqz a2,.Ldone - andi t1,a0,2 - beqz t1,.Lalignok - addu t0,a0,a2 # t0 is the "past the end" address - sh a1,0(a0) # store one halfword to get aligned - addu a0,2 - subu a2,2 -.Lalignok: - slti t1,a2,4 # .Laligned for 4 or more bytes - beqz t1,.Laligned - sne t1,a2,2 # one more halfword? - bnez t1,.Ldone - nop - sh a1,0(a0) -.Ldone: - j ra - nop - .set reorder -END(_memset16) - -/* - * void _memset32(uint32_t* dst, uint32_t value, size_t size); - */ - -LEAF(_memset32,0) - .set noreorder -DBG /* Check parameters */ -DBG andi t0,a0,3 # a0 must be word aligned -DBG tne t0,zero -DBG andi t2,a2,3 # a2 must be a multiple of 4 bytes -DBG tne t2,zero - -#ifdef FIXARGS - # ensure count is a multiple of 4 -#if (__mips==32) && (__mips_isa_rev>=2) - ins $a2,$0,0,2 -#else - ori a2,3 - xori a2,3 -#endif -#endif - - bnez a2,.Laligned # any work to do? - addu t0,a0,a2 # t0 is the "past the end" address - - j ra - nop - .set reorder -END(_memset32) - LEAF(memset,0) .set noreorder From 119cb55d43304579af741d8dbc3ca701b97bbf70 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 2 Apr 2015 12:02:55 -0700 Subject: [PATCH 056/123] gtest_repeat should allow negative values. If you pass in a negative value to gtest_repeat, it should run forever. The new runner didn't allow this, now it does. Change-Id: Ie0002c12e2fdad22b264adca37c165cfcfe05c7a --- tests/gtest_main.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp index bf2b69578..692b7e883 100644 --- a/tests/gtest_main.cpp +++ b/tests/gtest_main.cpp @@ -277,8 +277,8 @@ static bool EnumerateTests(int argc, char** argv, std::vector& testcas // PrettyUnitTestResultPrinter. The reason for copy is that PrettyUnitTestResultPrinter // is defined and used in gtest.cc, which is hard to reuse. static void OnTestIterationStartPrint(const std::vector& testcase_list, size_t iteration, - size_t iteration_count) { - if (iteration_count > 1) { + int iteration_count) { + if (iteration_count != 1) { printf("\nRepeating all tests (iteration %zu) . . .\n\n", iteration); } ColoredPrintf(COLOR_GREEN, "[==========] "); @@ -743,7 +743,7 @@ static void CollectChildTestResult(const ChildProcInfo& child_proc, TestCase& te // makes deadlock to use fork in multi-thread. // Returns true if all tests run successfully, otherwise return false. static bool RunTestInSeparateProc(int argc, char** argv, std::vector& testcase_list, - size_t iteration_count, size_t job_count, + int iteration_count, size_t job_count, const std::string& xml_output_filename) { // Stop default result printer to avoid environment setup/teardown information for each test. testing::UnitTest::GetInstance()->listeners().Release( @@ -762,7 +762,9 @@ static bool RunTestInSeparateProc(int argc, char** argv, std::vector& bool all_tests_passed = true; - for (size_t iteration = 1; iteration <= iteration_count; ++iteration) { + for (size_t iteration = 1; + iteration_count < 0 || iteration <= static_cast(iteration_count); + ++iteration) { OnTestIterationStartPrint(testcase_list, iteration, iteration_count); int64_t iteration_start_time_ns = NanoTime(); time_t epoch_iteration_start_time = time(NULL); @@ -875,7 +877,7 @@ struct IsolationTestOptions { int test_warnline_ms; std::string gtest_color; bool gtest_print_time; - size_t gtest_repeat; + int gtest_repeat; std::string gtest_output; }; @@ -993,12 +995,9 @@ static bool PickOptions(std::vector& args, IsolationTestOptions& options) } else if (strcmp(args[i], "--gtest_print_time=0") == 0) { options.gtest_print_time = false; } else if (strncmp(args[i], "--gtest_repeat=", strlen("--gtest_repeat=")) == 0) { - int repeat = atoi(args[i] + strlen("--gtest_repeat=")); - if (repeat < 0) { - fprintf(stderr, "invalid gtest_repeat count: %d\n", repeat); - return false; - } - options.gtest_repeat = repeat; + // If the value of gtest_repeat is < 0, then it indicates the tests + // should be repeated forever. + options.gtest_repeat = atoi(args[i] + strlen("--gtest_repeat=")); // Remove --gtest_repeat=xx from arguments, so child process only run one iteration for a single test. args.erase(args.begin() + i); --i; From 76ac1acdacc045cf1e56504e011dca68137dcd61 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 1 Apr 2015 14:45:10 -0700 Subject: [PATCH 057/123] Look into local group on dlsym with RTLD_DEFAULT Fix dlsym to look into local group when called with RTLD_DEFAULT and RTLD_NEXT. Bug: 17512583 Change-Id: I541354e89539c712af2ea4ec751e546913027084 --- linker/dlfcn.cpp | 13 ++++-------- linker/linker.cpp | 38 ++++++++++++++++++++++++++++++---- linker/linker.h | 2 +- tests/dlfcn_test.cpp | 23 +++++++++++++++++++- tests/libs/Android.mk | 9 ++++++++ tests/libs/dlsym_from_this.cpp | 30 +++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 tests/libs/dlsym_from_this.cpp diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 64df7a570..479e8317e 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -101,16 +101,11 @@ void* dlsym(void* handle, const char* symbol) { soinfo* found = nullptr; ElfW(Sym)* sym = nullptr; - if (handle == RTLD_DEFAULT) { - sym = dlsym_linear_lookup(symbol, &found, nullptr); - } else if (handle == RTLD_NEXT) { - void* caller_addr = __builtin_return_address(0); - soinfo* si = find_containing_library(caller_addr); + void* caller_addr = __builtin_return_address(0); + soinfo* caller = find_containing_library(caller_addr); - sym = nullptr; - if (si && si->next) { - sym = dlsym_linear_lookup(symbol, &found, si->next); - } + if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) { + sym = dlsym_linear_lookup(symbol, &found, caller, handle); } else { sym = dlsym_handle_lookup(reinterpret_cast(handle), &found, symbol); } diff --git a/linker/linker.cpp b/linker/linker.cpp index ebf125e1f..002a4f969 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -738,15 +738,21 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { beginning of the global solist. Otherwise the search starts at the specified soinfo (for RTLD_NEXT). */ -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) { +ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle) { SymbolName symbol_name(name); - if (start == nullptr) { - start = solist; + soinfo* start = solist; + + if (handle == RTLD_NEXT) { + if (caller == nullptr || caller->next == nullptr) { + return nullptr; + } else { + start = caller->next; + } } ElfW(Sym)* s = nullptr; - for (soinfo* si = start; (s == nullptr) && (si != nullptr); si = si->next) { + for (soinfo* si = start; si != nullptr; si = si->next) { if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) { continue; } @@ -758,6 +764,30 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) } } + // If not found - look into local_group unless + // caller is part of the global group in which + // case we already did it. + if (s == nullptr && caller != nullptr && + (caller->get_rtld_flags() & RTLD_GLOBAL) == 0) { + soinfo* local_group_root = caller->get_local_group_root(); + + if (handle == RTLD_DEFAULT) { + start = local_group_root; + } + + for (soinfo* si = start; si != nullptr; si = si->next) { + if (si->get_local_group_root() != local_group_root) { + break; + } + + s = si->find_symbol_by_name(symbol_name); + if (s != nullptr) { + *found = si; + break; + } + } + } + if (s != nullptr) { TRACE_TYPE(LOOKUP, "%s s->st_value = %p, found->base = %p", name, reinterpret_cast(s->st_value), reinterpret_cast((*found)->base)); diff --git a/linker/linker.h b/linker/linker.h index bf3e7bf5d..ec3d8f051 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -351,7 +351,7 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo); void do_dlclose(soinfo* si); -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start); +ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle); soinfo* find_containing_library(const void* addr); ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 1061e84f2..a63c070cf 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -46,7 +46,7 @@ TEST(dlfcn, ctor_function_call) { ASSERT_EQ(17, g_ctor_function_called); } -TEST(dlfcn, dlsym_in_self) { +TEST(dlfcn, dlsym_in_executable) { dlerror(); // Clear any pending errors. void* self = dlopen(NULL, RTLD_NOW); ASSERT_TRUE(self != NULL); @@ -64,6 +64,27 @@ TEST(dlfcn, dlsym_in_self) { ASSERT_EQ(0, dlclose(self)); } +TEST(dlfcn, dlsym_from_sofile) { + void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_LAZY | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + // check that we cant find '_test_dlsym_symbol' via dlsym(RTLD_DEFAULT) + void* symbol = dlsym(RTLD_DEFAULT, "test_dlsym_symbol"); + ASSERT_TRUE(symbol == nullptr); + ASSERT_SUBSTR("undefined symbol: test_dlsym_symbol", dlerror()); + + typedef int* (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "lookup_dlsym_symbol_using_RTLD_DEFAULT")); + + ASSERT_TRUE(fn != nullptr) << dlerror(); + + int* ptr = fn(); + ASSERT_TRUE(ptr != nullptr) << dlerror(); + ASSERT_EQ(42, *ptr); + + dlclose(handle); +} + TEST(dlfcn, dlsym_with_dependencies) { void* handle = dlopen("libtest_with_dependency.so", RTLD_NOW); ASSERT_TRUE(handle != NULL); diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 665ce0cb7..eb0a52a15 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -370,6 +370,15 @@ libtest_dlsym_weak_func_src_files := \ module := libtest_dlsym_weak_func include $(LOCAL_PATH)/Android.build.testlib.mk +# ----------------------------------------------------------------------------- +# Library to check RTLD_LOCAL with dlsym in 'this' +# ----------------------------------------------------------------------------- +libtest_dlsym_from_this_src_files := dlsym_from_this.cpp + +module := libtest_dlsym_from_this + +include $(LOCAL_PATH)/Android.build.testlib.mk + # ----------------------------------------------------------------------------- # Library with weak undefined function # ----------------------------------------------------------------------------- diff --git a/tests/libs/dlsym_from_this.cpp b/tests/libs/dlsym_from_this.cpp new file mode 100644 index 000000000..b5215c941 --- /dev/null +++ b/tests/libs/dlsym_from_this.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +int test_dlsym_symbol = 42; + +extern "C" int* lookup_dlsym_symbol_using_RTLD_DEFAULT() { + dlerror(); + int* result = static_cast(dlsym(RTLD_DEFAULT, "test_dlsym_symbol")); + // TODO: remove this once b/20049306 is fixed + if (result == nullptr) { + printf("Cannot find the answer\n"); + } + return result; +} + From 9b82136b987bc01224e3b42732334ea27c97d188 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 2 Apr 2015 16:03:56 -0700 Subject: [PATCH 058/123] Add ANDROID_DLEXT_FORCE_LOAD flag This flag allows to force loading of the library in the case when for some reason multiple ELF files share the same filename (because the already-loaded library has been removed and overwritten, for example). Change-Id: I798d44409ee13d63eaa75d685e99c4d028d2b0c1 --- libc/include/android/dlext.h | 16 ++++++++++++++-- linker/linker.cpp | 22 ++++++++++++---------- tests/dlext_test.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 90daf30eb..f10a8a2a8 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -59,16 +59,28 @@ enum { /* If opening a library using library_fd read it starting at library_fd_offset. * This flag is only valid when ANDROID_DLEXT_USE_LIBRARY_FD is set. */ - ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET = 0x20, + /* When set, do not check if the library has already been loaded by file stat(2)s. + * + * This flag allows forced loading of the library in the case when for some + * reason multiple ELF files share the same filename (because the already-loaded + * library has been removed and overwritten, for example). + * + * Note that if the library has the same dt_soname as an old one and some other + * library has the soname in DT_NEEDED list, the first one will be used to resolve any + * dependencies. + */ + ANDROID_DLEXT_FORCE_LOAD = 0x40, + /* Mask of valid bits */ ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT | ANDROID_DLEXT_WRITE_RELRO | ANDROID_DLEXT_USE_RELRO | ANDROID_DLEXT_USE_LIBRARY_FD | - ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET, + ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET | + ANDROID_DLEXT_FORCE_LOAD, }; typedef struct { diff --git a/linker/linker.cpp b/linker/linker.cpp index ebf125e1f..8703e4fe5 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1014,16 +1014,18 @@ static soinfo* load_library(LoadTaskList& load_tasks, } // Check for symlink and other situations where - // file can have different names. - for (soinfo* si = solist; si != nullptr; si = si->next) { - if (si->get_st_dev() != 0 && - si->get_st_ino() != 0 && - si->get_st_dev() == file_stat.st_dev && - si->get_st_ino() == file_stat.st_ino && - si->get_file_offset() == file_offset) { - TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " - "will return existing soinfo", name, si->name); - return si; + // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set + if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) { + for (soinfo* si = solist; si != nullptr; si = si->next) { + if (si->get_st_dev() != 0 && + si->get_st_ino() != 0 && + si->get_st_dev() == file_stat.st_dev && + si->get_st_ino() == file_stat.st_ino && + si->get_file_offset() == file_offset) { + TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " + "will return existing soinfo", name, si->name); + return si; + } } } diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index ca6a75a95..700abff5e 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -173,6 +173,40 @@ TEST_F(DlExtTest, ExtInfoUseOffsetWihtoutFd) { ASSERT_STREQ("dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20", dlerror()); } +TEST(dlext, android_dlopen_ext_force_load_smoke) { + // 1. Open actual file + void* handle = dlopen("libdlext_test.so", RTLD_NOW); + ASSERT_DL_NOTNULL(handle); + // 2. Open link with force_load flag set + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; + void* handle2 = android_dlopen_ext("libdlext_test_v2.so", RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle2); + ASSERT_TRUE(handle != handle2); + + dlclose(handle2); + dlclose(handle); +} + +TEST(dlext, android_dlopen_ext_force_load_soname_exception) { + // Check if soname lookup still returns already loaded library + // when ANDROID_DLEXT_FORCE_LOAD flag is specified. + void* handle = dlopen("libdlext_test_v2.so", RTLD_NOW); + ASSERT_DL_NOTNULL(handle); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; + + // Note that 'libdlext_test.so' is dt_soname for libdlext_test_v2.so + void* handle2 = android_dlopen_ext("libdlext_test.so", RTLD_NOW, &extinfo); + + ASSERT_DL_NOTNULL(handle2); + ASSERT_TRUE(handle == handle2); + + dlclose(handle2); + dlclose(handle); +} + TEST(dlfcn, dlopen_from_zip_absolute_path) { const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; From 76e1cbca75e9fdaf7a7943f2c58e65433b283f4a Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 Apr 2015 11:27:53 -0700 Subject: [PATCH 059/123] Fix volantis boot. NVIDIA binary blobs are assuming that __cache_clear, _Unwind_Backtrace, and _Unwind_GetIP are all in some library that they link, but now we've cleaned up this leakage, they're no longer getting it. Deliberately leak the symbols from libc.so until we get new blobs. Bug: http://b/20065774 Change-Id: I92ef07b2bce8d1ad719bf40dab41d745cd6904d4 --- libc/Android.mk | 3 ++- libc/arch-arm64/arm64.mk | 2 ++ libc/arch-arm64/bionic/libgcc_compat.c | 11 +++++++++++ libc/version_script.txt | 5 +++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 libc/arch-arm64/bionic/libgcc_compat.c diff --git a/libc/Android.mk b/libc/Android.mk index 0de0fb21c..e632ee72f 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1384,7 +1384,8 @@ LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt # We'd really like to do this for all architectures, but since this wasn't done # before, these symbols must continue to be exported on LP32 for binary # compatibility. -LOCAL_LDFLAGS_64 := -Wl,--exclude-libs,libgcc.a +# TODO: disabled for http://b/20065774. +#LOCAL_LDFLAGS_64 := -Wl,--exclude-libs,libgcc.a # TODO: This is to work around b/19059885. Remove after root cause is fixed LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv diff --git a/libc/arch-arm64/arm64.mk b/libc/arch-arm64/arm64.mk index 470a03838..6a2f31378 100644 --- a/libc/arch-arm64/arm64.mk +++ b/libc/arch-arm64/arm64.mk @@ -40,6 +40,8 @@ libc_bionic_src_files_arm64 += \ arch-arm64/bionic/syscall.S \ arch-arm64/bionic/vfork.S \ +# Work around for http://b/20065774. +libc_bionic_src_files_arm64 += arch-arm64/bionic/libgcc_compat.c libc_crt_target_cflags_arm64 := \ -I$(LOCAL_PATH)/arch-arm64/include diff --git a/libc/arch-arm64/bionic/libgcc_compat.c b/libc/arch-arm64/bionic/libgcc_compat.c new file mode 100644 index 000000000..35158cef6 --- /dev/null +++ b/libc/arch-arm64/bionic/libgcc_compat.c @@ -0,0 +1,11 @@ +/* STOPSHIP: remove this once the flounder blobs have been rebuilt (http://b/20065774). */ + +extern void __clear_cache(char*, char*); +extern char _Unwind_Backtrace; +extern char _Unwind_GetIP; + +void* __bionic_libgcc_compat_symbols[] = { + &__clear_cache, + &_Unwind_Backtrace, + &_Unwind_GetIP, +}; diff --git a/libc/version_script.txt b/libc/version_script.txt index afc5e5c47..349a2fc2c 100644 --- a/libc/version_script.txt +++ b/libc/version_script.txt @@ -1,4 +1,9 @@ LIBC { + global: + /* Work-around for http://b/20065774. */ + __clear_cache; + _Unwind_Backtrace; + _Unwind_GetIP; local: _ZSt7nothrow; _ZdaPv; From 0975a5d9d29019e764dc0ab2ddc75759bebffb9b Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 Apr 2015 13:09:10 -0700 Subject: [PATCH 060/123] Fix clang build. Apparently clang really doesn't want you to take the address of a builtin. Since this is only a temporary hack, let's just shrug and accept that clang-built volantis images won't work until we have new NVIDIA blobs. Bug: http://b/20065774 Change-Id: I4c8e893b15a1af8f9c54d3f89bfef112b63d09b4 --- libc/arch-arm64/bionic/libgcc_compat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libc/arch-arm64/bionic/libgcc_compat.c b/libc/arch-arm64/bionic/libgcc_compat.c index 35158cef6..904cc16af 100644 --- a/libc/arch-arm64/bionic/libgcc_compat.c +++ b/libc/arch-arm64/bionic/libgcc_compat.c @@ -1,5 +1,7 @@ /* STOPSHIP: remove this once the flounder blobs have been rebuilt (http://b/20065774). */ +#if defined(__clang__) + extern void __clear_cache(char*, char*); extern char _Unwind_Backtrace; extern char _Unwind_GetIP; @@ -9,3 +11,5 @@ void* __bionic_libgcc_compat_symbols[] = { &_Unwind_Backtrace, &_Unwind_GetIP, }; + +#endif From dffd3c58389103d6ee712fac6544217c00c1f315 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 Apr 2015 13:37:18 -0700 Subject: [PATCH 061/123] Try again to fix clang build. I got the condition the wrong way round in the previous change. Bug: http://b/20065774 Change-Id: I218b224b37f5fb2f7c7ec2f9af27472b340c4b15 --- libc/arch-arm64/bionic/libgcc_compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/arch-arm64/bionic/libgcc_compat.c b/libc/arch-arm64/bionic/libgcc_compat.c index 904cc16af..2dae3f552 100644 --- a/libc/arch-arm64/bionic/libgcc_compat.c +++ b/libc/arch-arm64/bionic/libgcc_compat.c @@ -1,6 +1,6 @@ /* STOPSHIP: remove this once the flounder blobs have been rebuilt (http://b/20065774). */ -#if defined(__clang__) +#if !defined(__clang__) extern void __clear_cache(char*, char*); extern char _Unwind_Backtrace; From 45c57131d25c82d6026ed36a791e9f5be40d5d7e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 3 Apr 2015 15:17:52 -0700 Subject: [PATCH 062/123] Add libdl dependency for target libraries Fixed x86_64 target build with gcc 4.9 Change-Id: Id3b1e9286c2bcbb8e3ac117bcef957304db7cfd3 --- tests/libs/Android.mk | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index eb0a52a15..da3fb1e46 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -376,6 +376,7 @@ include $(LOCAL_PATH)/Android.build.testlib.mk libtest_dlsym_from_this_src_files := dlsym_from_this.cpp module := libtest_dlsym_from_this +libtest_dlsym_from_this_shared_libraries_target := libdl include $(LOCAL_PATH)/Android.build.testlib.mk @@ -396,13 +397,9 @@ libtest_dlopen_from_ctor_src_files := \ module := libtest_dlopen_from_ctor -build_target := SHARED_LIBRARY -build_type := host -include $(TEST_PATH)/Android.build.mk +libtest_dlopen_from_ctor_shared_libraries_target := libdl -libtest_dlopen_from_ctor_shared_libraries := libdl -build_type := target -include $(TEST_PATH)/Android.build.mk +include $(LOCAL_PATH)/Android.build.testlib.mk # ----------------------------------------------------------------------------- # Library that depends on the library with constructor that calls dlopen() b/7941716 From f796985923e2d8308e00ed9567f36546dafb98d7 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 2 Apr 2015 17:47:48 -0700 Subject: [PATCH 063/123] Fix bug for recursive/errorcheck mutex on 32-bit devices. Bug: 19216648 Change-Id: I3b43b2d18d25b9bde352da1e35f9568133dec7cf --- libc/bionic/pthread_mutex.cpp | 26 ++++++++++++++++- tests/Android.mk | 10 +++++++ tests/pthread_test.cpp | 53 +++++++++++++++++++++++++--------- tests/stack_protector_test.cpp | 6 +--- 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index 5bdc5ed98..4fec7535c 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -392,6 +392,30 @@ static inline __always_inline int __recursive_increment(pthread_mutex_internal_t return 0; } +static inline __always_inline int __recursive_or_errorcheck_mutex_wait( + pthread_mutex_internal_t* mutex, + uint16_t shared, + uint16_t old_state, + const timespec* rel_timeout) { +// __futex_wait always waits on a 32-bit value. But state is 16-bit. For a normal mutex, the owner_tid +// field in mutex is not used. On 64-bit devices, the __pad field in mutex is not used. +// But when a recursive or errorcheck mutex is used on 32-bit devices, we need to add the +// owner_tid value in the value argument for __futex_wait, otherwise we may always get EAGAIN error. + +#if defined(__LP64__) + return __futex_wait_ex(&mutex->state, shared, old_state, rel_timeout); + +#else + // This implementation works only when the layout of pthread_mutex_internal_t matches below expectation. + // And it is based on the assumption that Android is always in little-endian devices. + static_assert(offsetof(pthread_mutex_internal_t, state) == 0, ""); + static_assert(offsetof(pthread_mutex_internal_t, owner_tid) == 2, ""); + + uint32_t owner_tid = atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed); + return __futex_wait_ex(&mutex->state, shared, (owner_tid << 16) | old_state, rel_timeout); +#endif +} + static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, const timespec* abs_timeout_or_null, clockid_t clock) { uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); @@ -469,7 +493,7 @@ static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex, return ETIMEDOUT; } } - if (__futex_wait_ex(&mutex->state, shared, old_state, rel_timeout) == -ETIMEDOUT) { + if (__recursive_or_errorcheck_mutex_wait(mutex, shared, old_state, rel_timeout) == -ETIMEDOUT) { return ETIMEDOUT; } old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); diff --git a/tests/Android.mk b/tests/Android.mk index 995877eaf..c94237593 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -128,6 +128,9 @@ libBionicStandardTests_c_includes := \ bionic/libc \ external/tinyxml2 \ +libBionicStandardTests_static_libraries := \ + libbase \ + libBionicStandardTests_ldlibs_host := \ -lrt \ @@ -257,6 +260,7 @@ bionic-unit-tests_whole_static_libraries := \ bionic-unit-tests_static_libraries := \ libtinyxml2 \ liblog \ + libbase \ # TODO: Include __cxa_thread_atexit_test.cpp to glibc tests once it is upgraded (glibc 2.18+) bionic-unit-tests_src_files := \ @@ -317,6 +321,7 @@ bionic-unit-tests-static_static_libraries := \ libdl \ libtinyxml2 \ liblog \ + libbase \ bionic-unit-tests-static_force_static_executable := true @@ -355,6 +360,11 @@ bionic-unit-tests-glibc_whole_static_libraries := \ libBionicGtestMain \ $(fortify_libs) \ +bionic-unit-tests-glibc_static_libraries := \ + libbase \ + liblog \ + libcutils \ + bionic-unit-tests-glibc_ldlibs := \ -lrt -ldl -lutil \ diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 5ab1f116b..f96ccf9d0 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -29,13 +29,19 @@ #include #include +#include #include +#include +#include + #include "private/bionic_macros.h" #include "private/ScopeGuard.h" #include "BionicDeathTest.h" #include "ScopedSignalHandler.h" +extern "C" pid_t gettid(); + TEST(pthread, pthread_key_create) { pthread_key_t key; ASSERT_EQ(0, pthread_key_create(&key, NULL)); @@ -704,6 +710,23 @@ TEST(pthread, pthread_rwlock_smoke) { ASSERT_EQ(0, pthread_rwlock_destroy(&l)); } +static void WaitUntilThreadSleep(std::atomic& pid) { + while (pid == 0) { + usleep(1000); + } + std::string filename = android::base::StringPrintf("/proc/%d/stat", pid.load()); + std::regex regex {R"(\s+S\s+)"}; + + while (true) { + std::string content; + ASSERT_TRUE(android::base::ReadFileToString(filename, &content)); + if (std::regex_search(content, regex)) { + break; + } + usleep(1000); + } +} + struct RwlockWakeupHelperArg { pthread_rwlock_t lock; enum Progress { @@ -713,9 +736,11 @@ struct RwlockWakeupHelperArg { LOCK_ACCESSED }; std::atomic progress; + std::atomic tid; }; static void pthread_rwlock_reader_wakeup_writer_helper(RwlockWakeupHelperArg* arg) { + arg->tid = gettid(); ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress); arg->progress = RwlockWakeupHelperArg::LOCK_WAITING; @@ -732,14 +757,14 @@ TEST(pthread, pthread_rwlock_reader_wakeup_writer) { ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL)); ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock)); wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED; + wakeup_arg.tid = 0; pthread_t thread; ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast(pthread_rwlock_reader_wakeup_writer_helper), &wakeup_arg)); - while (wakeup_arg.progress != RwlockWakeupHelperArg::LOCK_WAITING) { - usleep(5000); - } - usleep(5000); + WaitUntilThreadSleep(wakeup_arg.tid); + ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress); + wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_RELEASED; ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock)); @@ -749,6 +774,7 @@ TEST(pthread, pthread_rwlock_reader_wakeup_writer) { } static void pthread_rwlock_writer_wakeup_reader_helper(RwlockWakeupHelperArg* arg) { + arg->tid = gettid(); ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress); arg->progress = RwlockWakeupHelperArg::LOCK_WAITING; @@ -765,14 +791,14 @@ TEST(pthread, pthread_rwlock_writer_wakeup_reader) { ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL)); ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock)); wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED; + wakeup_arg.tid = 0; pthread_t thread; ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast(pthread_rwlock_writer_wakeup_reader_helper), &wakeup_arg)); - while (wakeup_arg.progress != RwlockWakeupHelperArg::LOCK_WAITING) { - usleep(5000); - } - usleep(5000); + WaitUntilThreadSleep(wakeup_arg.tid); + ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress); + wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_RELEASED; ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock)); @@ -1263,7 +1289,6 @@ TEST(pthread, pthread_mutex_init_same_as_static_initializers) { ASSERT_EQ(0, memcmp(&lock_recursive, &m3.lock, sizeof(pthread_mutex_t))); ASSERT_EQ(0, pthread_mutex_destroy(&lock_recursive)); } - class MutexWakeupHelper { private: PthreadMutex m; @@ -1274,8 +1299,10 @@ class MutexWakeupHelper { LOCK_ACCESSED }; std::atomic progress; + std::atomic tid; static void thread_fn(MutexWakeupHelper* helper) { + helper->tid = gettid(); ASSERT_EQ(LOCK_INITIALIZED, helper->progress); helper->progress = LOCK_WAITING; @@ -1293,15 +1320,15 @@ class MutexWakeupHelper { void test() { ASSERT_EQ(0, pthread_mutex_lock(&m.lock)); progress = LOCK_INITIALIZED; + tid = 0; pthread_t thread; ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast(MutexWakeupHelper::thread_fn), this)); - while (progress != LOCK_WAITING) { - usleep(5000); - } - usleep(5000); + WaitUntilThreadSleep(tid); + ASSERT_EQ(LOCK_WAITING, progress); + progress = LOCK_RELEASED; ASSERT_EQ(0, pthread_mutex_unlock(&m.lock)); diff --git a/tests/stack_protector_test.cpp b/tests/stack_protector_test.cpp index 80077113b..22285d123 100644 --- a/tests/stack_protector_test.cpp +++ b/tests/stack_protector_test.cpp @@ -24,14 +24,10 @@ #include #include #include -#include #include #include -#if defined(__GLIBC__) -// glibc doesn't expose gettid(2). -pid_t gettid() { return syscall(__NR_gettid); } -#endif // __GLIBC__ +extern "C" pid_t gettid(); // For x86, bionic and glibc have per-thread stack guard values (all identical). #if defined(__i386__) From 694fd2d880634f23254582ab1161bffbfd104696 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Sun, 5 Apr 2015 10:51:56 -0700 Subject: [PATCH 064/123] Add a test for getdelim(3) on a directory. Apparently uClibc has a bug here. We don't, but let's keep it that way. Bug: http://landley.net/notes.html#21-03-2015 Change-Id: If406df963db9bee47921d7a1c116ebcab08d96bf --- tests/stdio_test.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index 2ecfc6072..62677cdff 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -151,6 +151,15 @@ TEST(stdio, getdelim_invalid) { fclose(fp); } +TEST(stdio, getdelim_directory) { + FILE* fp = fopen("/proc", "r"); + ASSERT_TRUE(fp != NULL); + char* word_read; + size_t allocated_length; + ASSERT_EQ(-1, getdelim(&word_read, &allocated_length, ' ', fp)); + fclose(fp); +} + TEST(stdio, getline) { FILE* fp = tmpfile(); ASSERT_TRUE(fp != NULL); From aae859cc3ca127d890e853cbf12b731e05624a22 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 31 Mar 2015 11:14:03 -0700 Subject: [PATCH 065/123] Add realpath for soinfo This change adds realpath to soinfo and extends limit on filenames from 128 to PATH_MAX. It also removes soinfo::name field, linker uses dt_soname instead. Bug: http://b/19818481 Bug: https://code.google.com/p/android/issues/detail?id=80336 Change-Id: I9cff4cb5bda3ee2bc74e1bbded9594ea7fbe2a08 --- linker/dlfcn.cpp | 35 +++--- linker/linker.cpp | 248 +++++++++++++++++++++++++---------------- linker/linker.h | 19 +++- linker/linker_mips.cpp | 4 +- tests/dlext_test.cpp | 4 +- tests/dlfcn_test.cpp | 55 +++++---- tests/utils.h | 84 ++++++++++++++ 7 files changed, 313 insertions(+), 136 deletions(-) create mode 100644 tests/utils.h diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 479e8317e..5ed88914c 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -136,7 +136,7 @@ int dladdr(const void* addr, Dl_info* info) { memset(info, 0, sizeof(Dl_info)); - info->dli_fname = si->name; + info->dli_fname = si->get_realpath(); // Address at which the shared object is loaded. info->dli_fbase = reinterpret_cast(si->base); @@ -228,23 +228,28 @@ static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 }; static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; #endif -static soinfo __libdl_info("libdl.so", nullptr, 0, RTLD_GLOBAL); +static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); +static soinfo* __libdl_info = nullptr; // This is used by the dynamic linker. Every process gets these symbols for free. soinfo* get_libdl_info() { - if ((__libdl_info.flags_ & FLAG_LINKED) == 0) { - __libdl_info.flags_ |= FLAG_LINKED; - __libdl_info.strtab_ = ANDROID_LIBDL_STRTAB; - __libdl_info.symtab_ = g_libdl_symtab; - __libdl_info.nbucket_ = sizeof(g_libdl_buckets)/sizeof(unsigned); - __libdl_info.nchain_ = sizeof(g_libdl_chains)/sizeof(unsigned); - __libdl_info.bucket_ = g_libdl_buckets; - __libdl_info.chain_ = g_libdl_chains; - __libdl_info.ref_count_ = 1; - __libdl_info.strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB); - __libdl_info.local_group_root_ = &__libdl_info; - __libdl_info.soname_ = "libdl.so"; + if (__libdl_info == nullptr) { + __libdl_info = new (__libdl_info_buf) soinfo("libdl.so", nullptr, 0, RTLD_GLOBAL); + __libdl_info->flags_ |= FLAG_LINKED; + __libdl_info->strtab_ = ANDROID_LIBDL_STRTAB; + __libdl_info->symtab_ = g_libdl_symtab; + __libdl_info->nbucket_ = sizeof(g_libdl_buckets)/sizeof(unsigned); + __libdl_info->nchain_ = sizeof(g_libdl_chains)/sizeof(unsigned); + __libdl_info->bucket_ = g_libdl_buckets; + __libdl_info->chain_ = g_libdl_chains; + __libdl_info->ref_count_ = 1; + __libdl_info->strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB); + __libdl_info->local_group_root_ = __libdl_info; + __libdl_info->soname_ = "libdl.so"; +#if defined(__arm__) + strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_)); +#endif } - return &__libdl_info; + return __libdl_info; } diff --git a/linker/linker.cpp b/linker/linker.cpp index a9c2bc1e5..3c8ba76e6 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -153,7 +153,8 @@ static void insert_soinfo_into_debug_map(soinfo* info) { // Copy the necessary fields into the debug structure. link_map* map = &(info->link_map_head); map->l_addr = info->load_bias; - map->l_name = info->name; + // link_map l_name field is not const. + map->l_name = const_cast(info->get_realpath()); map->l_ld = info->dynamic; // Stick the new library at the end of the list. @@ -238,7 +239,7 @@ void SoinfoListAllocator::free(LinkedListEntry* entry) { static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags) { - if (strlen(name) >= SOINFO_NAME_LEN) { + if (strlen(name) >= PATH_MAX) { DL_ERR("library name \"%s\" too long", name); return nullptr; } @@ -263,7 +264,7 @@ static void soinfo_free(soinfo* si) { soinfo *prev = nullptr, *trav; - TRACE("name %s: freeing soinfo @ %p", si->name, si); + TRACE("name %s: freeing soinfo @ %p", si->get_soname(), si); for (trav = solist; trav != nullptr; trav = trav->next) { if (trav == si) { @@ -274,7 +275,7 @@ static void soinfo_free(soinfo* si) { if (trav == nullptr) { // si was not in solist - DL_ERR("name \"%s\"@%p is not in solist!", si->name, si); + DL_ERR("name \"%s\"@%p is not in solist!", si->get_soname(), si); return; } @@ -324,6 +325,17 @@ static void parse_LD_PRELOAD(const char* path) { parse_path(path, " :", &g_ld_preload_names); } +static bool realpath_fd(int fd, std::string* realpath) { + std::vector buf(PATH_MAX), proc_self_fd(PATH_MAX); + snprintf(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd); + if (readlink(&proc_self_fd[0], &buf[0], buf.size()) == -1) { + return false; + } + + *realpath = std::string(&buf[0]); + return true; +} + #if defined(__arm__) // For a given PC, find the .so that it belongs to. @@ -377,7 +389,7 @@ static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { return s->st_shndx != SHN_UNDEF; } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) { DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'", - ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->name); + ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_soname()); } return false; @@ -392,12 +404,12 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num]; TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)", - symbol_name.get_name(), name, reinterpret_cast(base)); + symbol_name.get_name(), get_soname(), reinterpret_cast(base)); // test against bloom filter if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", - symbol_name.get_name(), name, reinterpret_cast(base)); + symbol_name.get_name(), get_soname(), reinterpret_cast(base)); return nullptr; } @@ -407,7 +419,7 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { if (n == 0) { TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", - symbol_name.get_name(), name, reinterpret_cast(base)); + symbol_name.get_name(), get_soname(), reinterpret_cast(base)); return nullptr; } @@ -418,14 +430,14 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", - symbol_name.get_name(), name, reinterpret_cast(s->st_value), + symbol_name.get_name(), get_soname(), reinterpret_cast(s->st_value), static_cast(s->st_size)); return s; } } while ((gnu_chain_[n++] & 1) == 0); TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", - symbol_name.get_name(), name, reinterpret_cast(base)); + symbol_name.get_name(), get_soname(), reinterpret_cast(base)); return nullptr; } @@ -434,30 +446,36 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { uint32_t hash = symbol_name.elf_hash(); TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd", - symbol_name.get_name(), name, reinterpret_cast(base), hash, hash % nbucket_); + symbol_name.get_name(), get_soname(), + reinterpret_cast(base), hash, hash % nbucket_); for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { ElfW(Sym)* s = symtab_ + n; if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", - symbol_name.get_name(), name, reinterpret_cast(s->st_value), - static_cast(s->st_size)); + symbol_name.get_name(), get_soname(), + reinterpret_cast(s->st_value), + static_cast(s->st_size)); return s; } } TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", - symbol_name.get_name(), name, reinterpret_cast(base), hash, hash % nbucket_); + symbol_name.get_name(), get_soname(), + reinterpret_cast(base), hash, hash % nbucket_); return nullptr; } -soinfo::soinfo(const char* name, const struct stat* file_stat, +soinfo::soinfo(const char* realpath, const struct stat* file_stat, off64_t file_offset, int rtld_flags) { memset(this, 0, sizeof(*this)); - strlcpy(this->name, name, sizeof(this->name)); + if (realpath != nullptr) { + realpath_ = realpath; + } + flags_ = FLAG_NEW_SOINFO; version_ = SOINFO_VERSION; @@ -473,7 +491,7 @@ soinfo::soinfo(const char* name, const struct stat* file_stat, uint32_t SymbolName::elf_hash() { if (!has_elf_hash_) { - const unsigned char* name = reinterpret_cast(name_); + const uint8_t* name = reinterpret_cast(name_); uint32_t h = 0, g; while (*name) { @@ -493,7 +511,7 @@ uint32_t SymbolName::elf_hash() { uint32_t SymbolName::gnu_hash() { if (!has_gnu_hash_) { uint32_t h = 5381; - const unsigned char* name = reinterpret_cast(name_); + const uint8_t* name = reinterpret_cast(name_); while (*name != 0) { h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c } @@ -522,7 +540,7 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found * relocations for -Bsymbolic linked dynamic executables. */ if (si_from->has_DT_SYMBOLIC) { - DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name); + DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name); s = si_from->find_symbol_by_name(symbol_name); if (s != nullptr) { *si_found_in = si_from; @@ -532,7 +550,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found // 1. Look for it in global_group if (s == nullptr) { global_group.visit([&](soinfo* global_si) { - DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name); + DEBUG("%s: looking up %s in %s (from global group)", + si_from->get_soname(), name, global_si->get_soname()); s = global_si->find_symbol_by_name(symbol_name); if (s != nullptr) { *si_found_in = global_si; @@ -551,7 +570,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found return true; } - DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name); + DEBUG("%s: looking up %s in %s (from local group)", + si_from->get_soname(), name, local_si->get_soname()); s = local_si->find_symbol_by_name(symbol_name); if (s != nullptr) { *si_found_in = local_si; @@ -565,8 +585,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found if (s != nullptr) { TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, " "found in %s, base = %p, load bias = %p", - si_from->name, name, reinterpret_cast(s->st_value), - (*si_found_in)->name, reinterpret_cast((*si_found_in)->base), + si_from->get_soname(), name, reinterpret_cast(s->st_value), + (*si_found_in)->get_soname(), reinterpret_cast((*si_found_in)->base), reinterpret_cast((*si_found_in)->load_bias)); } @@ -922,7 +942,7 @@ static bool format_path(char* buf, size_t buf_size, const char* path, const char static int open_library_on_default_path(const char* name, off64_t* file_offset) { for (size_t i = 0; kDefaultLdPaths[i] != nullptr; ++i) { char buf[512]; - if(!format_path(buf, sizeof(buf), kDefaultLdPaths[i], name)) { + if (!format_path(buf, sizeof(buf), kDefaultLdPaths[i], name)) { continue; } @@ -1053,7 +1073,7 @@ static soinfo* load_library(LoadTaskList& load_tasks, si->get_st_ino() == file_stat.st_ino && si->get_file_offset() == file_offset) { TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " - "will return existing soinfo", name, si->name); + "will return existing soinfo", name, si->get_realpath()); return si; } } @@ -1064,13 +1084,19 @@ static soinfo* load_library(LoadTaskList& load_tasks, return nullptr; } + std::string realpath = name; + if (!realpath_fd(fd, &realpath)) { + PRINT("cannot resolve realpath for the library \"%s\": %s", name, strerror(errno)); + realpath = name; + } + // Read the ELF header and load the segments. - ElfReader elf_reader(name, fd, file_offset); + ElfReader elf_reader(realpath.c_str(), fd, file_offset); if (!elf_reader.Load(extinfo)) { return nullptr; } - soinfo* si = soinfo_alloc(name, &file_stat, file_offset, rtld_flags); + soinfo* si = soinfo_alloc(realpath.c_str(), &file_stat, file_offset, rtld_flags); if (si == nullptr) { return nullptr; } @@ -1275,7 +1301,7 @@ static void soinfo_unload(soinfo* root) { } if (!root->can_unload()) { - TRACE("not unloading '%s' - the binary is flagged with NODELETE", root->name); + TRACE("not unloading '%s' - the binary is flagged with NODELETE", root->get_soname()); return; } @@ -1298,7 +1324,7 @@ static void soinfo_unload(soinfo* root) { if (si->has_min_version(0)) { soinfo* child = nullptr; while ((child = si->get_children().pop_front()) != nullptr) { - TRACE("%s@%p needs to unload %s@%p", si->name, si, child->name, child); + TRACE("%s@%p needs to unload %s@%p", si->get_soname(), si, child->get_soname(), child); if (local_unload_list.contains(child)) { continue; } else if (child->is_linked() && child->get_local_group_root() != root) { @@ -1308,17 +1334,20 @@ static void soinfo_unload(soinfo* root) { } } } else { -#ifdef __LP64__ - __libc_fatal("soinfo for \"%s\"@%p has no version", si->name, si); +#if !defined(__arm__) + __libc_fatal("soinfo for \"%s\"@%p has no version", si->get_soname(), si); #else - PRINT("warning: soinfo for \"%s\"@%p has no version", si->name, si); + PRINT("warning: soinfo for \"%s\"@%p has no version", si->get_soname(), si); for_each_dt_needed(si, [&] (const char* library_name) { - TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name); + TRACE("deprecated (old format of soinfo): %s needs to unload %s", + si->get_soname(), library_name); + soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr); if (needed != nullptr) { // Not found: for example if symlink was deleted between dlopen and dlclose // Since we cannot really handle errors at this point - print and continue. - PRINT("warning: couldn't find %s needed by %s on unload.", library_name, si->name); + PRINT("warning: couldn't find %s needed by %s on unload.", + library_name, si->get_soname()); return; } else if (local_unload_list.contains(needed)) { // already visited @@ -1348,7 +1377,7 @@ static void soinfo_unload(soinfo* root) { soinfo_unload(si); } } else { - TRACE("not unloading '%s' group, decrementing ref_count to %zd", root->name, ref_count); + TRACE("not unloading '%s' group, decrementing ref_count to %zd", root->get_soname(), ref_count); } } @@ -1447,7 +1476,7 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa const char* sym_name = nullptr; ElfW(Addr) addend = get_addend(rel, reloc); - DEBUG("Processing '%s' relocation at index %zd", this->name, idx); + DEBUG("Processing '%s' relocation at index %zd", get_soname(), idx); if (type == R_GENERIC_NONE) { continue; } @@ -1462,7 +1491,7 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa // We only allow an undefined symbol if this is a weak reference... s = &symtab_[sym]; if (ELF_ST_BIND(s->st_info) != STB_WEAK) { - DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name); + DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname()); return false; } @@ -1652,13 +1681,13 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa /* * ET_EXEC is not supported so this should not happen. * - * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf * - * Section 4.7.1.10 "Dynamic relocations" + * Section 4.6.11 "Dynamic relocations" * R_AARCH64_COPY may only appear in executable objects where e_type is * set to ET_EXEC. */ - DL_ERR("%s R_AARCH64_COPY relocations are not supported", name); + DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_soname()); return false; case R_AARCH64_TLS_TPREL64: TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n", @@ -1711,11 +1740,11 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa * * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf * - * Section 4.7.1.10 "Dynamic relocations" + * Section 4.6.1.10 "Dynamic relocations" * R_ARM_COPY may only appear in executable objects where e_type is * set to ET_EXEC. */ - DL_ERR("%s R_ARM_COPY relocations are not supported", name); + DL_ERR("%s R_ARM_COPY relocations are not supported", get_soname()); return false; #elif defined(__i386__) case R_386_32: @@ -1747,7 +1776,7 @@ void soinfo::call_array(const char* array_name __unused, linker_function_t* func return; } - TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, name); + TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, get_soname()); int begin = reverse ? (count - 1) : 0; int end = reverse ? -1 : count; @@ -1758,7 +1787,7 @@ void soinfo::call_array(const char* array_name __unused, linker_function_t* func call_function("function", functions[i]); } - TRACE("[ Done calling %s for '%s' ]", array_name, name); + TRACE("[ Done calling %s for '%s' ]", array_name, get_soname()); } void soinfo::call_function(const char* function_name __unused, linker_function_t function) { @@ -1766,9 +1795,9 @@ void soinfo::call_function(const char* function_name __unused, linker_function_t return; } - TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, name); + TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, get_soname()); function(); - TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, name); + TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, get_soname()); } void soinfo::call_pre_init_constructors() { @@ -1797,14 +1826,14 @@ void soinfo::call_constructors() { if (!is_main_executable() && preinit_array_ != nullptr) { // The GNU dynamic linker silently ignores these, but we warn the developer. PRINT("\"%s\": ignoring %zd-entry DT_PREINIT_ARRAY in shared library!", - name, preinit_array_count_); + get_soname(), preinit_array_count_); } get_children().for_each([] (soinfo* si) { si->call_constructors(); }); - TRACE("\"%s\": calling constructors", name); + TRACE("\"%s\": calling constructors", get_soname()); // DT_INIT should be called before DT_INIT_ARRAY if both are present. call_function("DT_INIT", init_func_); @@ -1815,7 +1844,7 @@ void soinfo::call_destructors() { if (!constructors_called) { return; } - TRACE("\"%s\": calling destructors", name); + TRACE("\"%s\": calling destructors", get_soname()); // DT_FINI_ARRAY must be parsed in reverse order. call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true); @@ -1912,12 +1941,28 @@ void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { } } -const char* soinfo::get_soname() { +const char* soinfo::get_realpath() const { +#if defined(__arm__) + if (has_min_version(2)) { + return realpath_.c_str(); + } else { + return old_name_; + } +#else + return realpath_.c_str(); +#endif +} + +const char* soinfo::get_soname() const { +#if defined(__arm__) if (has_min_version(2)) { return soname_; } else { - return name; + return old_name_; } +#else + return soname_; +#endif } // This is a return on get_children()/get_parents() if @@ -1950,7 +1995,8 @@ ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) { const char* soinfo::get_string(ElfW(Word) index) const { if (has_min_version(1) && (index >= strtab_size_)) { - __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", name, strtab_size_, index); + __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", + get_soname(), strtab_size_, index); } return strtab_ + index; @@ -2065,13 +2111,13 @@ bool soinfo::prelink_image() { /* We can't log anything until the linker is relocated */ bool relocating_linker = (flags_ & FLAG_LINKER) != 0; if (!relocating_linker) { - INFO("[ linking %s ]", name); + INFO("[ linking %s ]", get_soname()); DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast(base), flags_); } if (dynamic == nullptr) { if (!relocating_linker) { - DL_ERR("missing PT_DYNAMIC in \"%s\"", name); + DL_ERR("missing PT_DYNAMIC in \"%s\"", get_soname()); } return false; } else { @@ -2120,7 +2166,7 @@ bool soinfo::prelink_image() { if (!powerof2(gnu_maskwords_)) { DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", - gnu_maskwords_, name); + gnu_maskwords_, get_realpath()); return false; } --gnu_maskwords_; @@ -2142,7 +2188,8 @@ bool soinfo::prelink_image() { case DT_SYMENT: if (d->d_un.d_val != sizeof(ElfW(Sym))) { - DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", static_cast(d->d_un.d_val), name); + DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", + static_cast(d->d_un.d_val), get_realpath()); return false; } break; @@ -2150,12 +2197,12 @@ bool soinfo::prelink_image() { case DT_PLTREL: #if defined(USE_RELA) if (d->d_un.d_val != DT_RELA) { - DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_RELA", name); + DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_RELA", get_realpath()); return false; } #else if (d->d_un.d_val != DT_REL) { - DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_REL", name); + DL_ERR("unsupported DT_PLTREL in \"%s\"; expected DT_REL", get_realpath()); return false; } #endif @@ -2216,11 +2263,11 @@ bool soinfo::prelink_image() { break; case DT_ANDROID_REL: - DL_ERR("unsupported DT_ANDROID_REL in \"%s\"", name); + DL_ERR("unsupported DT_ANDROID_REL in \"%s\"", get_realpath()); return false; case DT_ANDROID_RELSZ: - DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", name); + DL_ERR("unsupported DT_ANDROID_RELSZ in \"%s\"", get_realpath()); return false; case DT_RELAENT: @@ -2235,11 +2282,11 @@ bool soinfo::prelink_image() { break; case DT_REL: - DL_ERR("unsupported DT_REL in \"%s\"", name); + DL_ERR("unsupported DT_REL in \"%s\"", get_realpath()); return false; case DT_RELSZ: - DL_ERR("unsupported DT_RELSZ in \"%s\"", name); + DL_ERR("unsupported DT_RELSZ in \"%s\"", get_realpath()); return false; #else @@ -2267,11 +2314,11 @@ bool soinfo::prelink_image() { break; case DT_ANDROID_RELA: - DL_ERR("unsupported DT_ANDROID_RELA in \"%s\"", name); + DL_ERR("unsupported DT_ANDROID_RELA in \"%s\"", get_realpath()); return false; case DT_ANDROID_RELASZ: - DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", name); + DL_ERR("unsupported DT_ANDROID_RELASZ in \"%s\"", get_realpath()); return false; // "Indicates that all RELATIVE relocations have been concatenated together, @@ -2283,27 +2330,27 @@ bool soinfo::prelink_image() { break; case DT_RELA: - DL_ERR("unsupported DT_RELA in \"%s\"", name); + DL_ERR("unsupported DT_RELA in \"%s\"", get_realpath()); return false; case DT_RELASZ: - DL_ERR("unsupported DT_RELASZ in \"%s\"", name); + DL_ERR("unsupported DT_RELASZ in \"%s\"", get_realpath()); return false; #endif case DT_INIT: init_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); - DEBUG("%s constructors (DT_INIT) found at %p", name, init_func_); + DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_); break; case DT_FINI: fini_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); - DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func_); + DEBUG("%s destructors (DT_FINI) found at %p", get_realpath(), fini_func_); break; case DT_INIT_ARRAY: init_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); - DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array_); + DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", get_realpath(), init_array_); break; case DT_INIT_ARRAYSZ: @@ -2312,7 +2359,7 @@ bool soinfo::prelink_image() { case DT_FINI_ARRAY: fini_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); - DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array_); + DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", get_realpath(), fini_array_); break; case DT_FINI_ARRAYSZ: @@ -2321,7 +2368,7 @@ bool soinfo::prelink_image() { case DT_PREINIT_ARRAY: preinit_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); - DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array_); + DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", get_realpath(), preinit_array_); break; case DT_PREINIT_ARRAYSZ: @@ -2330,7 +2377,7 @@ bool soinfo::prelink_image() { case DT_TEXTREL: #if defined(__LP64__) - DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name); + DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", get_realpath()); return false; #else has_text_relocations = true; @@ -2348,7 +2395,7 @@ bool soinfo::prelink_image() { case DT_FLAGS: if (d->d_un.d_val & DF_TEXTREL) { #if defined(__LP64__) - DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name); + DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", get_realpath()); return false; #else has_text_relocations = true; @@ -2415,7 +2462,7 @@ bool soinfo::prelink_image() { default: if (!relocating_linker) { - DL_WARN("%s: unused DT entry: type %p arg %p", name, + DL_WARN("%s: unused DT entry: type %p arg %p", get_realpath(), reinterpret_cast(d->d_tag), reinterpret_cast(d->d_un.d_val)); } break; @@ -2426,6 +2473,9 @@ bool soinfo::prelink_image() { for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { if (d->d_tag == DT_SONAME) { soname_ = get_string(d->d_un.d_val); +#if defined(__arm__) + strlcpy(old_name_, soname_, sizeof(old_name_)); +#endif break; } } @@ -2439,15 +2489,16 @@ bool soinfo::prelink_image() { return false; } if (nbucket_ == 0 && gnu_nbucket_ == 0) { - DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name); + DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" " + "(new hash type from the future?)", get_soname()); return false; } if (strtab_ == 0) { - DL_ERR("empty/missing DT_STRTAB in \"%s\"", name); + DL_ERR("empty/missing DT_STRTAB in \"%s\"", get_soname()); return false; } if (symtab_ == 0) { - DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name); + DL_ERR("empty/missing DT_SYMTAB in \"%s\"", get_soname()); return false; } return true; @@ -2466,10 +2517,10 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& // Make segments writable to allow text relocations to work properly. We will later call // phdr_table_protect_segments() after all of them are applied and all constructors are run. DL_WARN("%s has text relocations. This is wasting memory and prevents " - "security hardening. Please fix.", name); + "security hardening. Please fix.", get_soname()); if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { DL_ERR("can't unprotect loadable segments for \"%s\": %s", - name, strerror(errno)); + get_soname(), strerror(errno)); return false; } } @@ -2482,7 +2533,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& android_relocs_[1] == 'P' && (android_relocs_[2] == 'U' || android_relocs_[2] == 'S') && android_relocs_[3] == '2') { - DEBUG("[ android relocating %s ]", name); + DEBUG("[ android relocating %s ]", get_soname()); bool relocated = false; const uint8_t* packed_relocs = android_relocs_ + 4; @@ -2511,26 +2562,26 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& #if defined(USE_RELA) if (rela_ != nullptr) { - DEBUG("[ relocating %s ]", name); + DEBUG("[ relocating %s ]", get_soname()); if (!relocate(plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) { return false; } } if (plt_rela_ != nullptr) { - DEBUG("[ relocating %s plt ]", name); + DEBUG("[ relocating %s plt ]", get_soname()); if (!relocate(plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) { return false; } } #else if (rel_ != nullptr) { - DEBUG("[ relocating %s ]", name); + DEBUG("[ relocating %s ]", get_soname()); if (!relocate(plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) { return false; } } if (plt_rel_ != nullptr) { - DEBUG("[ relocating %s plt ]", name); + DEBUG("[ relocating %s plt ]", get_soname()); if (!relocate(plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) { return false; } @@ -2543,14 +2594,14 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& } #endif - DEBUG("[ finished linking %s ]", name); + DEBUG("[ finished linking %s ]", get_soname()); #if !defined(__LP64__) if (has_text_relocations) { // All relocations are done, we can protect our segments back to read-only. if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { DL_ERR("can't protect segments for \"%s\": %s", - name, strerror(errno)); + get_soname(), strerror(errno)); return false; } } @@ -2559,7 +2610,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& /* We can also turn on GNU RELRO protection */ if (phdr_table_protect_gnu_relro(phdr, phnum, load_bias) < 0) { DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", - name, strerror(errno)); + get_soname(), strerror(errno)); return false; } @@ -2568,14 +2619,14 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& if (phdr_table_serialize_gnu_relro(phdr, phnum, load_bias, extinfo->relro_fd) < 0) { DL_ERR("failed serializing GNU RELRO section for \"%s\": %s", - name, strerror(errno)); + get_soname(), strerror(errno)); return false; } } else if (extinfo && (extinfo->flags & ANDROID_DLEXT_USE_RELRO)) { if (phdr_table_map_gnu_relro(phdr, phnum, load_bias, extinfo->relro_fd) < 0) { DL_ERR("failed mapping GNU RELRO section for \"%s\": %s", - name, strerror(errno)); + get_soname(), strerror(errno)); return false; } } @@ -2617,7 +2668,12 @@ static void add_vdso(KernelArgumentBlock& args __unused) { #else #define LINKER_PATH "/system/bin/linker" #endif -static soinfo linker_soinfo_for_gdb(LINKER_PATH, nullptr, 0, 0); + +// This is done to avoid calling c-tor prematurely +// because soinfo c-tor needs memory allocator +// which might be initialized after global variables. +static uint8_t linker_soinfo_for_gdb_buf[sizeof(soinfo)] __attribute__((aligned(8))); +static soinfo* linker_soinfo_for_gdb = nullptr; /* gdb expects the linker to be in the debug shared object list. * Without this, gdb has trouble locating the linker's ".text" @@ -2627,7 +2683,9 @@ static soinfo linker_soinfo_for_gdb(LINKER_PATH, nullptr, 0, 0); * be on the soinfo list. */ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { - linker_soinfo_for_gdb.base = linker_base; + linker_soinfo_for_gdb = new (linker_soinfo_for_gdb_buf) soinfo(LINKER_PATH, nullptr, 0, 0); + + linker_soinfo_for_gdb->base = linker_base; /* * Set the dynamic field in the link map otherwise gdb will complain with @@ -2638,8 +2696,8 @@ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_base); ElfW(Phdr)* phdr = reinterpret_cast(linker_base + elf_hdr->e_phoff); phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, - &linker_soinfo_for_gdb.dynamic, nullptr); - insert_soinfo_into_debug_map(&linker_soinfo_for_gdb); + &linker_soinfo_for_gdb->dynamic, nullptr); + insert_soinfo_into_debug_map(linker_soinfo_for_gdb); } /* @@ -2837,7 +2895,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( fflush(stdout); #endif - TRACE("[ Ready to execute '%s' @ %p ]", si->name, reinterpret_cast(si->entry)); + TRACE("[ Ready to execute '%s' @ %p ]", si->get_soname(), reinterpret_cast(si->entry)); return si->entry; } @@ -2883,7 +2941,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_addr); ElfW(Phdr)* phdr = reinterpret_cast(linker_addr + elf_hdr->e_phoff); - soinfo linker_so("[dynamic linker]", nullptr, 0, 0); + soinfo linker_so(nullptr, nullptr, 0, 0); // If the linker is not acting as PT_INTERP entry_point is equal to // _start. Which means that the linker is running as an executable and diff --git a/linker/linker.h b/linker/linker.h index ec3d8f051..7482581d8 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -39,6 +39,8 @@ #include "private/libc_logging.h" #include "linked_list.h" +#include + #define DL_ERR(fmt, x...) \ do { \ __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \ @@ -94,7 +96,9 @@ #define SOINFO_VERSION 2 +#if defined(__arm__) #define SOINFO_NAME_LEN 128 +#endif typedef void (*linker_function_t)(); @@ -141,8 +145,11 @@ class SymbolName { struct soinfo { public: typedef LinkedList soinfo_list_t; +#if defined(__arm__) + private: + char old_name_[SOINFO_NAME_LEN]; +#endif public: - char name[SOINFO_NAME_LEN]; const ElfW(Phdr)* phdr; size_t phnum; ElfW(Addr) entry; @@ -263,8 +270,12 @@ struct soinfo { bool can_unload() const; bool is_gnu_hash() const; - bool inline has_min_version(uint32_t min_version) const { + bool inline has_min_version(uint32_t min_version __unused) const { +#if defined(__arm__) return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version; +#else + return true; +#endif } bool is_linked() const; @@ -279,7 +290,8 @@ struct soinfo { soinfo* get_local_group_root() const; - const char* get_soname(); + const char* get_soname() const; + const char* get_realpath() const; private: ElfW(Sym)* elf_lookup(SymbolName& symbol_name); @@ -327,6 +339,7 @@ struct soinfo { size_t android_relocs_size_; const char* soname_; + std::string realpath_; friend soinfo* get_libdl_info(); }; diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index 44d39fdef..14f6a1bfb 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -64,7 +64,7 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, ElfW(Addr) sym_addr = 0; const char* sym_name = nullptr; - DEBUG("Processing '%s' relocation at index %zd", this->name, idx); + DEBUG("Processing '%s' relocation at index %zd", get_soname(), idx); if (type == R_GENERIC_NONE) { continue; } @@ -77,7 +77,7 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group); if (s == nullptr) { // mips does not support relocation with weak-undefined symbols - DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name); + DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname()); return false; } else { // We got a definition. diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 700abff5e..70124180d 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -156,9 +156,9 @@ TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) { ASSERT_SUBSTR("dlopen failed: file offset for the library \"libname_placeholder\" is negative", dlerror()); extinfo.library_fd_offset = PAGE_SIZE; - handle_ = android_dlopen_ext("libname_placeholder", RTLD_NOW, &extinfo); + handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo); ASSERT_TRUE(handle_ == nullptr); - ASSERT_STREQ("dlopen failed: \"libname_placeholder\" has bad ELF magic", dlerror()); + ASSERT_EQ("dlopen failed: \"" + lib_path + "\" has bad ELF magic", dlerror()); close(extinfo.library_fd); } diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index a63c070cf..1d6242836 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -26,9 +26,12 @@ #include +#include "utils.h" + #define ASSERT_SUBSTR(needle, haystack) \ ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack) + static bool g_called = false; extern "C" void DlSymTestFunction() { g_called = true; @@ -699,7 +702,7 @@ TEST(dlfcn, dlsym_failures) { ASSERT_EQ(0, dlclose(self)); } -TEST(dlfcn, dladdr) { +TEST(dlfcn, dladdr_executable) { dlerror(); // Clear any pending errors. void* self = dlopen(NULL, RTLD_NOW); ASSERT_TRUE(self != NULL); @@ -720,13 +723,11 @@ TEST(dlfcn, dladdr) { rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path)); ASSERT_NE(rc, -1); executable_path[rc] = '\0'; - std::string executable_name(basename(executable_path)); // The filename should be that of this executable. - // Note that we don't know whether or not we have the full path, so we want an "ends_with" test. - std::string dli_fname(info.dli_fname); - dli_fname = basename(&dli_fname[0]); - ASSERT_EQ(dli_fname, executable_name); + char dli_realpath[PATH_MAX]; + ASSERT_TRUE(realpath(info.dli_fname, dli_realpath) != nullptr); + ASSERT_STREQ(executable_path, dli_realpath); // The symbol name should be the symbol we looked up. ASSERT_STREQ(info.dli_sname, "DlSymTestFunction"); @@ -734,22 +735,16 @@ TEST(dlfcn, dladdr) { // The address should be the exact address of the symbol. ASSERT_EQ(info.dli_saddr, sym); - // Look in /proc/pid/maps to find out what address we were loaded at. - // TODO: factor /proc/pid/maps parsing out into a class and reuse all over bionic. - void* base_address = NULL; - char line[BUFSIZ]; - FILE* fp = fopen("/proc/self/maps", "r"); - ASSERT_TRUE(fp != NULL); - while (fgets(line, sizeof(line), fp) != NULL) { - uintptr_t start = strtoul(line, 0, 16); - line[strlen(line) - 1] = '\0'; // Chomp the '\n'. - char* path = strchr(line, '/'); - if (path != NULL && strcmp(executable_path, path) == 0) { - base_address = reinterpret_cast(start); + std::vector maps; + ASSERT_TRUE(Maps::parse_maps(&maps)); + + void* base_address = nullptr; + for (const map_record& rec : maps) { + if (executable_path == rec.pathname) { + base_address = reinterpret_cast(rec.addr_start); break; } } - fclose(fp); // The base address should be the address we were loaded at. ASSERT_EQ(info.dli_fbase, base_address); @@ -757,6 +752,28 @@ TEST(dlfcn, dladdr) { ASSERT_EQ(0, dlclose(self)); } +#if defined(__LP64__) +#define BIONIC_PATH_TO_LIBC "/system/lib64/libc.so" +#else +#define BIONIC_PATH_TO_LIBC "/system/lib/libc.so" +#endif + +TEST(dlfcn, dladdr_libc) { +#if defined(__BIONIC__) + Dl_info info; + void* addr = reinterpret_cast(puts); // well-known libc function + ASSERT_TRUE(dladdr(addr, &info) != 0); + + ASSERT_STREQ(BIONIC_PATH_TO_LIBC, info.dli_fname); + // TODO: add check for dfi_fbase + ASSERT_STREQ("puts", info.dli_sname); + ASSERT_EQ(addr, info.dli_saddr); +#else + GTEST_LOG_(INFO) << "This test does nothing for glibc. Glibc returns path from ldconfig " + "for libc.so, which is symlink itself (not a realpath).\n"; +#endif +} + TEST(dlfcn, dladdr_invalid) { Dl_info info; diff --git a/tests/utils.h b/tests/utils.h new file mode 100644 index 000000000..bad5d8966 --- /dev/null +++ b/tests/utils.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_UTILS_H +#define __TEST_UTILS_H +#include +#include + +#include "private/ScopeGuard.h" + +struct map_record { + uintptr_t addr_start; + uintptr_t addr_end; + + int perms; + + size_t offset; + + dev_t device; + ino_t inode; + + std::string pathname; +}; + +class Maps { + public: + static bool parse_maps(std::vector* maps) { + char path[64]; + snprintf(path, sizeof(path), "/proc/self/task/%d/maps", getpid()); + FILE* fp = fopen(path, "re"); + if (fp == nullptr) { + return false; + } + + auto fp_guard = make_scope_guard([&]() { + fclose(fp); + }); + + char line[BUFSIZ]; + while (fgets(line, sizeof(line), fp) != nullptr) { + map_record record; + dev_t dev_major, dev_minor; + char pathstr[BUFSIZ]; + char prot[5]; // sizeof("rwxp") + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %x:%x %lu %s", + &record.addr_start, &record.addr_end, prot, &record.offset, + &dev_major, &dev_minor, &record.inode, pathstr) == 8) { + record.perms = 0; + if (prot[0] == 'r') { + record.perms |= PROT_READ; + } + if (prot[1] == 'w') { + record.perms |= PROT_WRITE; + } + if (prot[2] == 'x') { + record.perms |= PROT_EXEC; + } + + // TODO: parse shared/private? + + record.device = makedev(dev_major, dev_minor); + record.pathname = pathstr; + maps->push_back(record); + } + } + + return true; + } +}; + +#endif From dadac10fccb1558fb00bbafc3fc4f6b3a20f9591 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 6 Apr 2015 12:43:55 -0700 Subject: [PATCH 066/123] Reject changes with cleanspecs. Cleanspecs must not be removed once they have been built. This means they can't be reverted, or reliably cherry-picked. Just skip any changes that include them since they make such a mess. Change-Id: I3df8d81f93651d573485de7a75ecf5c6278c0001 --- tools/bionicbb/gerrit.py | 6 ++ tools/bionicbb/gmail_listener.py | 40 +++++++--- tools/bionicbb/test_gmail_listener.py | 102 ++++++++++++++------------ 3 files changed, 92 insertions(+), 56 deletions(-) diff --git a/tools/bionicbb/gerrit.py b/tools/bionicbb/gerrit.py index 40719b48a..9c62c6a47 100644 --- a/tools/bionicbb/gerrit.py +++ b/tools/bionicbb/gerrit.py @@ -29,6 +29,12 @@ def get_commit(change_id, revision): call('/changes/{}/revisions/{}/commit'.format(change_id, revision))) +def get_files_for_revision(change_id, revision): + return json.loads( + call('/changes/{}/revisions/{}/files'.format( + change_id, revision))).keys() + + def call(endpoint, method='GET'): if method != 'GET': raise NotImplementedError('Currently only HTTP GET is supported.') diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py index 770f0c414..3e501cca9 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/gmail_listener.py @@ -19,6 +19,7 @@ import httplib import httplib2 import jenkinsapi import json +import os import re import requests import termcolor @@ -51,15 +52,35 @@ def get_headers(msg): return headers -def should_skip_message(info): - if info['MessageType'] in ('newchange', 'newpatchset', 'comment'): - commit = gerrit.get_commit(info['Change-Id'], info['PatchSet']) - committer = commit['committer']['email'] - return not committer.endswith('@google.com') - else: - raise ValueError('should_skip_message() is only valid for new ' +def is_untrusted_committer(change_id, patch_set): + # TODO(danalbert): Needs to be based on the account that made the comment. + commit = gerrit.get_commit(change_id, patch_set) + committer = commit['committer']['email'] + return not committer.endswith('@google.com') + + +def contains_cleanspec(change_id, patch_set): + files = gerrit.get_files_for_revision(change_id, patch_set) + return 'CleanSpec.mk' in [os.path.basename(f) for f in files] + + +def should_skip_build(info): + if info['MessageType'] not in ('newchange', 'newpatchset', 'comment'): + raise ValueError('should_skip_build() is only valid for new ' 'changes, patch sets, and commits.') + change_id = info['Change-Id'] + patch_set = info['PatchSet'] + + checks = [ + is_untrusted_committer, + contains_cleanspec, + ] + for check in checks: + if check(change_id, patch_set): + return True + return False + def build_service(): from apiclient.discovery import build @@ -214,7 +235,7 @@ def build_project(gerrit_info, dry_run, lunch_target=None): def handle_change(gerrit_info, _, dry_run): - if should_skip_message(gerrit_info): + if should_skip_build(gerrit_info): return True return build_project(gerrit_info, dry_run) handle_newchange = handle_change @@ -246,8 +267,7 @@ def handle_comment(gerrit_info, body, dry_run): if 'Verified+1' in body: drop_rejection(gerrit_info, dry_run) - # TODO(danalbert): Needs to be based on the account that made the comment. - if should_skip_message(gerrit_info): + if should_skip_build(gerrit_info): return True command_map = { diff --git a/tools/bionicbb/test_gmail_listener.py b/tools/bionicbb/test_gmail_listener.py index 6545cdc1d..af9eda094 100644 --- a/tools/bionicbb/test_gmail_listener.py +++ b/tools/bionicbb/test_gmail_listener.py @@ -3,61 +3,71 @@ import mock import unittest -class TestShouldSkipMessage(unittest.TestCase): - def test_accepts_googlers(self): +class TestShouldSkipBuild(unittest.TestCase): + @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('gerrit.get_commit') + def test_accepts_googlers(self, mock_commit, *other_checks): + mock_commit.return_value = { + 'committer': {'email': 'googler@google.com'} + } + + for other_check in other_checks: + other_check.return_value = False + for message_type in ('newchange', 'newpatchset', 'comment'): - with mock.patch('gerrit.get_commit') as mock_commit: - mock_commit.return_value = { - 'committer': {'email': 'googler@google.com'} - } + self.assertFalse(gmail_listener.should_skip_build({ + 'MessageType': message_type, + 'Change-Id': '', + 'PatchSet': '', + })) - self.assertFalse(gmail_listener.should_skip_message({ - 'MessageType': message_type, - 'Change-Id': '', - 'PatchSet': '', - })) + @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('gerrit.get_commit') + def test_rejects_googlish_domains(self, mock_commit, *other_checks): + mock_commit.return_value = { + 'committer': {'email': 'fakegoogler@google.com.fake.com'} + } + + for other_check in other_checks: + other_check.return_value = False - def test_rejects_non_googlers(self): for message_type in ('newchange', 'newpatchset', 'comment'): - with mock.patch('gerrit.get_commit') as mock_commit: - mock_commit.return_value = { - 'committer': {'email': 'fakegoogler@google.com.fake.com'} - } + self.assertTrue(gmail_listener.should_skip_build({ + 'MessageType': message_type, + 'Change-Id': '', + 'PatchSet': '', + })) - self.assertTrue(gmail_listener.should_skip_message({ - 'MessageType': message_type, - 'Change-Id': '', - 'PatchSet': '', - })) + @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('gerrit.get_commit') + def test_rejects_non_googlers(self, mock_commit, *other_checks): + mock_commit.return_value = { + 'committer': {'email': 'johndoe@example.com'} + } - with mock.patch('gerrit.get_commit') as mock_commit: - mock_commit.return_value = { - 'committer': {'email': 'johndoe@example.com'} - } + for other_check in other_checks: + other_check.return_value = False - self.assertTrue(gmail_listener.should_skip_message({ - 'MessageType': message_type, - 'Change-Id': '', - 'PatchSet': '', - })) - - def test_calls_gerrit_get_commit(self): # pylint: disable=no-self-use for message_type in ('newchange', 'newpatchset', 'comment'): - with mock.patch('gerrit.get_commit') as mock_commit: - gmail_listener.should_skip_message({ - 'MessageType': message_type, - 'Change-Id': 'foo', - 'PatchSet': 'bar', - }) - mock_commit.assert_called_once_with('foo', 'bar') + self.assertTrue(gmail_listener.should_skip_build({ + 'MessageType': message_type, + 'Change-Id': '', + 'PatchSet': '', + })) - with mock.patch('gerrit.get_commit') as mock_commit: - gmail_listener.should_skip_message({ - 'MessageType': message_type, - 'Change-Id': 'baz', - 'PatchSet': 'qux', - }) - mock_commit.assert_called_once_with('baz', 'qux') + @mock.patch('gmail_listener.is_untrusted_committer') + @mock.patch('gerrit.get_files_for_revision') + def test_skips_cleanspecs(self, mock_files, *other_checks): + mock_files.return_value = ['foo/CleanSpec.mk'] + for other_check in other_checks: + other_check.return_value = False + + for message_type in ('newchange', 'newpatchset', 'comment'): + self.assertTrue(gmail_listener.should_skip_build({ + 'MessageType': message_type, + 'Change-Id': '', + 'PatchSet': '', + })) if __name__ == '__main__': From cd13b14e98d4921af126667fae0cf6613a5615c5 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 3 Apr 2015 18:41:42 -0700 Subject: [PATCH 067/123] Stop libc from cross-referencing unwind symbols This makes unwind symbols 'protected', which should prevent them from relocating against libc++.so/libcutls.so. This is temporary file and it is going to be removed once libc.so stops exporting them. Bug: http://b/19958712 Change-Id: I96a765afe47e68d2e2ceb288870e63a25ca52081 --- libc/arch-arm/arm.mk | 1 + libc/arch-arm/bionic/libgcc_protect_unwind.c | 93 ++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 libc/arch-arm/bionic/libgcc_protect_unwind.c diff --git a/libc/arch-arm/arm.mk b/libc/arch-arm/arm.mk index d72a160d0..6ef81bb78 100644 --- a/libc/arch-arm/arm.mk +++ b/libc/arch-arm/arm.mk @@ -39,6 +39,7 @@ libc_bionic_src_files_arm += \ arch-arm/bionic/__bionic_clone.S \ arch-arm/bionic/_exit_with_stack_teardown.S \ arch-arm/bionic/libgcc_compat.c \ + arch-arm/bionic/libgcc_protect_unwind.c \ arch-arm/bionic/__restore.S \ arch-arm/bionic/setjmp.S \ arch-arm/bionic/syscall.S \ diff --git a/libc/arch-arm/bionic/libgcc_protect_unwind.c b/libc/arch-arm/bionic/libgcc_protect_unwind.c new file mode 100644 index 000000000..6d758fcfc --- /dev/null +++ b/libc/arch-arm/bionic/libgcc_protect_unwind.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: This file should go away once unwinder migration to libc++.so is complete. + +extern char _Unwind_Backtrace __attribute((visibility("protected"))); +extern char __gnu_Unwind_Find_exidx __attribute((visibility("protected"))); +extern char __gnu_Unwind_Restore_VFP_D __attribute((visibility("protected"))); +extern char __gnu_Unwind_Restore_VFP __attribute((visibility("protected"))); +extern char __gnu_Unwind_Restore_VFP_D_16_to_31 __attribute((visibility("protected"))); +extern char __gnu_Unwind_Restore_WMMXD __attribute((visibility("protected"))); +extern char __gnu_Unwind_Restore_WMMXC __attribute((visibility("protected"))); +extern char _Unwind_GetCFA __attribute((visibility("protected"))); +extern char __gnu_Unwind_RaiseException __attribute((visibility("protected"))); +extern char __gnu_Unwind_ForcedUnwind __attribute((visibility("protected"))); +extern char __gnu_Unwind_Resume __attribute((visibility("protected"))); +extern char __gnu_Unwind_Resume_or_Rethrow __attribute((visibility("protected"))); +extern char _Unwind_Complete __attribute((visibility("protected"))); +extern char _Unwind_DeleteException __attribute((visibility("protected"))); +extern char _Unwind_VRS_Get __attribute((visibility("protected"))); +extern char _Unwind_VRS_Set __attribute((visibility("protected"))); +extern char __gnu_Unwind_Backtrace __attribute((visibility("protected"))); +extern char _Unwind_VRS_Pop __attribute((visibility("protected"))); +extern char __gnu_Unwind_Save_VFP_D __attribute((visibility("protected"))); +extern char __gnu_Unwind_Save_VFP __attribute((visibility("protected"))); +extern char __gnu_Unwind_Save_VFP_D_16_to_31 __attribute((visibility("protected"))); +extern char __gnu_Unwind_Save_WMMXD __attribute((visibility("protected"))); +extern char __gnu_Unwind_Save_WMMXC __attribute((visibility("protected"))); +extern char ___Unwind_RaiseException __attribute((visibility("protected"))); +extern char _Unwind_RaiseException __attribute((visibility("protected"))); +extern char ___Unwind_Resume __attribute((visibility("protected"))); +extern char _Unwind_Resume __attribute((visibility("protected"))); +extern char ___Unwind_Resume_or_Rethrow __attribute((visibility("protected"))); +extern char _Unwind_Resume_or_Rethrow __attribute((visibility("protected"))); +extern char ___Unwind_ForcedUnwind __attribute((visibility("protected"))); +extern char _Unwind_ForcedUnwind __attribute((visibility("protected"))); +extern char ___Unwind_Backtrace __attribute((visibility("protected"))); +extern char _Unwind_GetRegionStart __attribute((visibility("protected"))); +extern char _Unwind_GetLanguageSpecificData __attribute((visibility("protected"))); +extern char _Unwind_GetDataRelBase __attribute((visibility("protected"))); +extern char _Unwind_GetTextRelBase __attribute((visibility("protected"))); + +void* __bionic_libgcc_unwind_symbols[] = { + &_Unwind_Backtrace, + &__gnu_Unwind_Find_exidx, + &__gnu_Unwind_Restore_VFP_D, + &__gnu_Unwind_Restore_VFP, + &__gnu_Unwind_Restore_VFP_D_16_to_31, + &__gnu_Unwind_Restore_WMMXD, + &__gnu_Unwind_Restore_WMMXC, + &_Unwind_GetCFA, + &__gnu_Unwind_RaiseException, + &__gnu_Unwind_ForcedUnwind, + &__gnu_Unwind_Resume, + &__gnu_Unwind_Resume_or_Rethrow, + &_Unwind_Complete, + &_Unwind_DeleteException, + &_Unwind_VRS_Get, + &_Unwind_VRS_Set, + &__gnu_Unwind_Backtrace, + &_Unwind_VRS_Pop, + &__gnu_Unwind_Save_VFP_D, + &__gnu_Unwind_Save_VFP, + &__gnu_Unwind_Save_VFP_D_16_to_31, + &__gnu_Unwind_Save_WMMXD, + &__gnu_Unwind_Save_WMMXC, + &___Unwind_RaiseException, + &_Unwind_RaiseException, + &___Unwind_Resume, + &_Unwind_Resume, + &___Unwind_Resume_or_Rethrow, + &_Unwind_Resume_or_Rethrow, + &___Unwind_ForcedUnwind, + &_Unwind_ForcedUnwind, + &___Unwind_Backtrace, + &_Unwind_GetRegionStart, + &_Unwind_GetLanguageSpecificData, + &_Unwind_GetDataRelBase, + &_Unwind_GetTextRelBase, +}; From 1dce3ed24940808a553f4da1a92a9b6759efd4ab Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 6 Apr 2015 19:05:58 -0700 Subject: [PATCH 068/123] Fix x86_64 build Change-Id: Id81c059bf3ecdb9cc84d04d16ffda34baaadc5a1 --- tests/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils.h b/tests/utils.h index bad5d8966..fd012a345 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -52,7 +52,7 @@ class Maps { char line[BUFSIZ]; while (fgets(line, sizeof(line), fp) != nullptr) { map_record record; - dev_t dev_major, dev_minor; + uint32_t dev_major, dev_minor; char pathstr[BUFSIZ]; char prot[5]; // sizeof("rwxp") if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %x:%x %lu %s", From 76615dae93c18ac890e167c547a08c0228709a33 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 17 Mar 2015 14:22:09 -0700 Subject: [PATCH 069/123] Provide writer preference option in rwlock. Previous implementation of rwlock contains four atomic variables, which is hard to maintain and change. So I make following changes in this CL: 1. Add pending flags in rwlock.state, so we don't need to synchronize between different atomic variables. Using compare_and_swap operations on rwlock.state is enough for all state change. 2. Add pending_lock to protect readers/writers waiting and wake up operations. As waiting/wakeup is not performance critical, using a lock is easier to maintain. 3. Add writer preference option. 4. Add unit tests for rwlock. Bug: 19109156 Change-Id: Idcaa58d695ea401d64445610b465ac5cff23ec7c --- libc/bionic/pthread_rwlock.cpp | 469 ++++++++++++++++++++++----------- libc/include/pthread.h | 9 +- libc/private/bionic_lock.h | 74 ++++++ tests/pthread_test.cpp | 138 +++++++++- 4 files changed, 532 insertions(+), 158 deletions(-) create mode 100644 libc/private/bionic_lock.h diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp index 8aa40ae0a..934210eb4 100644 --- a/libc/bionic/pthread_rwlock.cpp +++ b/libc/bionic/pthread_rwlock.cpp @@ -28,9 +28,11 @@ #include #include +#include #include "pthread_internal.h" #include "private/bionic_futex.h" +#include "private/bionic_lock.h" #include "private/bionic_time_conversions.h" /* Technical note: @@ -53,18 +55,39 @@ * - This implementation will return EDEADLK in "write after write" and "read after * write" cases and will deadlock in write after read case. * - * TODO: As it stands now, pending_readers and pending_writers could be merged into a - * a single waiters variable. Keeping them separate adds a bit of clarity and keeps - * the door open for a writer-biased implementation. - * */ -#define RWLOCKATTR_DEFAULT 0 -#define RWLOCKATTR_SHARED_MASK 0x0010 +// A rwlockattr is implemented as a 32-bit integer which has following fields: +// bits name description +// 1 rwlock_kind have rwlock preference like PTHREAD_RWLOCK_PREFER_READER_NP. +// 0 process_shared set to 1 if the rwlock is shared between processes. + +#define RWLOCKATTR_PSHARED_SHIFT 0 +#define RWLOCKATTR_KIND_SHIFT 1 + +#define RWLOCKATTR_PSHARED_MASK 1 +#define RWLOCKATTR_KIND_MASK 2 +#define RWLOCKATTR_RESERVED_MASK (~3) + +static inline __always_inline __always_inline bool __rwlockattr_getpshared(const pthread_rwlockattr_t* attr) { + return (*attr & RWLOCKATTR_PSHARED_MASK) >> RWLOCKATTR_PSHARED_SHIFT; +} + +static inline __always_inline __always_inline void __rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared) { + *attr = (*attr & ~RWLOCKATTR_PSHARED_MASK) | (pshared << RWLOCKATTR_PSHARED_SHIFT); +} + +static inline __always_inline int __rwlockattr_getkind(const pthread_rwlockattr_t* attr) { + return (*attr & RWLOCKATTR_KIND_MASK) >> RWLOCKATTR_KIND_SHIFT; +} + +static inline __always_inline void __rwlockattr_setkind(pthread_rwlockattr_t* attr, int kind) { + *attr = (*attr & ~RWLOCKATTR_KIND_MASK) | (kind << RWLOCKATTR_KIND_SHIFT); +} int pthread_rwlockattr_init(pthread_rwlockattr_t* attr) { - *attr = PTHREAD_PROCESS_PRIVATE; + *attr = 0; return 0; } @@ -73,40 +96,121 @@ int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr) { return 0; } +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) { + if (__rwlockattr_getpshared(attr)) { + *pshared = PTHREAD_PROCESS_SHARED; + } else { + *pshared = PTHREAD_PROCESS_PRIVATE; + } + return 0; +} + int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared) { switch (pshared) { case PTHREAD_PROCESS_PRIVATE: + __rwlockattr_setpshared(attr, 0); + return 0; case PTHREAD_PROCESS_SHARED: - *attr = pshared; + __rwlockattr_setpshared(attr, 1); return 0; default: return EINVAL; } } -int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) { - *pshared = *attr; +int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t* attr, int* pref) { + *pref = __rwlockattr_getkind(attr); return 0; } -struct pthread_rwlock_internal_t { - atomic_int state; // 0=unlock, -1=writer lock, +n=reader lock - atomic_int writer_thread_id; - atomic_uint pending_readers; - atomic_uint pending_writers; - int32_t attr; - - bool process_shared() const { - return attr == PTHREAD_PROCESS_SHARED; +int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref) { + switch (pref) { + case PTHREAD_RWLOCK_PREFER_READER_NP: // Fall through. + case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP: + __rwlockattr_setkind(attr, pref); + return 0; + default: + return EINVAL; } +} + +// A rwlock state is implemented as a 32-bit integer which has following rules: +// bits name description +// 31 owned_by_writer_flag set to 1 if the lock is owned by a writer now. +// 30-2 reader_count the count of readers holding the lock. +// 1 have_pending_writers set to 1 if having pending writers. +// 0 have_pending_readers set to 1 if having pending readers. + +#define STATE_HAVE_PENDING_READERS_SHIFT 0 +#define STATE_HAVE_PENDING_WRITERS_SHIFT 1 +#define STATE_READER_COUNT_SHIFT 2 +#define STATE_OWNED_BY_WRITER_SHIFT 31 + +#define STATE_HAVE_PENDING_READERS_FLAG (1 << STATE_HAVE_PENDING_READERS_SHIFT) +#define STATE_HAVE_PENDING_WRITERS_FLAG (1 << STATE_HAVE_PENDING_WRITERS_SHIFT) +#define STATE_READER_COUNT_CHANGE_STEP (1 << STATE_READER_COUNT_SHIFT) +#define STATE_OWNED_BY_WRITER_FLAG (1 << STATE_OWNED_BY_WRITER_SHIFT) + +#define STATE_HAVE_PENDING_READERS_OR_WRITERS_FLAG \ + (STATE_HAVE_PENDING_READERS_FLAG | STATE_HAVE_PENDING_WRITERS_FLAG) + +struct pthread_rwlock_internal_t { + atomic_int state; + atomic_int writer_tid; + + bool pshared; + bool writer_nonrecursive_preferred; + uint16_t __pad; + +// When a reader thread plans to suspend on the rwlock, it will add STATE_HAVE_PENDING_READERS_FLAG +// in state, increase pending_reader_count, and wait on pending_reader_wakeup_serial. After woken +// up, the reader thread decreases pending_reader_count, and the last pending reader thread should +// remove STATE_HAVE_PENDING_READERS_FLAG in state. A pending writer thread works in a similar way, +// except that it uses flag and members for writer threads. + + Lock pending_lock; // All pending members below are protected by pending_lock. + uint32_t pending_reader_count; // Count of pending reader threads. + uint32_t pending_writer_count; // Count of pending writer threads. + uint32_t pending_reader_wakeup_serial; // Pending reader threads wait on this address by futex_wait. + uint32_t pending_writer_wakeup_serial; // Pending writer threads wait on this address by futex_wait. #if defined(__LP64__) - char __reserved[36]; -#else char __reserved[20]; +#else + char __reserved[4]; #endif }; +static inline __always_inline bool __state_owned_by_writer(int state) { + return state < 0; +} + +static inline __always_inline bool __state_owned_by_readers(int state) { + // If state >= 0, the owned_by_writer_flag is not set. + // And if state >= STATE_READER_COUNT_CHANGE_STEP, the reader_count field is not empty. + return state >= STATE_READER_COUNT_CHANGE_STEP; +} + +static inline __always_inline bool __state_owned_by_readers_or_writer(int state) { + return state < 0 || state >= STATE_READER_COUNT_CHANGE_STEP; +} + +static inline __always_inline int __state_add_writer_flag(int state) { + return state | STATE_OWNED_BY_WRITER_FLAG; +} + +static inline __always_inline bool __state_is_last_reader(int state) { + return (state >> STATE_READER_COUNT_SHIFT) == 1; +} + +static inline __always_inline bool __state_have_pending_writers(int state) { + return state & STATE_HAVE_PENDING_WRITERS_FLAG; +} + +static inline __always_inline bool __state_have_pending_readers_or_writers(int state) { + return state & STATE_HAVE_PENDING_READERS_OR_WRITERS_FLAG; +} + static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t), "pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation."); @@ -115,31 +219,35 @@ static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t), static_assert(alignof(pthread_rwlock_t) == 4, "pthread_rwlock_t should fulfill the alignment requirement of pthread_rwlock_internal_t."); -static inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) { +static inline __always_inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) { return reinterpret_cast(rwlock_interface); } int pthread_rwlock_init(pthread_rwlock_t* rwlock_interface, const pthread_rwlockattr_t* attr) { pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); - if (__predict_true(attr == NULL)) { - rwlock->attr = 0; - } else { - switch (*attr) { - case PTHREAD_PROCESS_SHARED: - case PTHREAD_PROCESS_PRIVATE: - rwlock->attr= *attr; + memset(rwlock, 0, sizeof(pthread_rwlock_internal_t)); + + if (__predict_false(attr != NULL)) { + rwlock->pshared = __rwlockattr_getpshared(attr); + int kind = __rwlockattr_getkind(attr); + switch (kind) { + case PTHREAD_RWLOCK_PREFER_READER_NP: + rwlock->writer_nonrecursive_preferred = false; + break; + case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP: + rwlock->writer_nonrecursive_preferred = true; break; default: return EINVAL; } + if ((*attr & RWLOCKATTR_RESERVED_MASK) != 0) { + return EINVAL; + } } atomic_init(&rwlock->state, 0); - atomic_init(&rwlock->writer_thread_id, 0); - atomic_init(&rwlock->pending_readers, 0); - atomic_init(&rwlock->pending_writers, 0); - + rwlock->pending_lock.init(rwlock->pshared); return 0; } @@ -152,105 +260,173 @@ int pthread_rwlock_destroy(pthread_rwlock_t* rwlock_interface) { return 0; } +static inline __always_inline bool __can_acquire_read_lock(int old_state, + bool writer_nonrecursive_preferred) { + // If writer is preferred with nonrecursive reader, we prevent further readers from acquiring + // the lock when there are writers waiting for the lock. + bool cannot_apply = __state_owned_by_writer(old_state) || + (writer_nonrecursive_preferred && __state_have_pending_writers(old_state)); + return !cannot_apply; +} + +static inline __always_inline int __pthread_rwlock_tryrdlock(pthread_rwlock_internal_t* rwlock) { + int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); + + while (__predict_true(__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred))) { + + int new_state = old_state + STATE_READER_COUNT_CHANGE_STEP; + if (__predict_false(!__state_owned_by_readers(new_state))) { // Happens when reader count overflows. + return EAGAIN; + } + if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, new_state, + memory_order_acquire, memory_order_relaxed))) { + return 0; + } + } + return EBUSY; +} + static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock, const timespec* abs_timeout_or_null) { - if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id, - memory_order_relaxed))) { + if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) { return EDEADLK; } while (true) { + int ret = __pthread_rwlock_tryrdlock(rwlock); + if (ret == 0 || ret == EAGAIN) { + return ret; + } + int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); - if (__predict_true(old_state >= 0)) { - if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, old_state + 1, - memory_order_acquire, memory_order_relaxed)) { - return 0; - } - } else { - timespec ts; - timespec* rel_timeout = NULL; + if (__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) { + continue; + } - if (abs_timeout_or_null != NULL) { - rel_timeout = &ts; - if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) { - return ETIMEDOUT; - } - } + timespec ts; + timespec* rel_timeout = NULL; - // To avoid losing wake ups, the pending_readers increment should be observed before - // futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used - // here. Because only a seq_cst fence can ensure sequential consistency for non-atomic - // operations in futex_wait. - atomic_fetch_add_explicit(&rwlock->pending_readers, 1, memory_order_relaxed); - - atomic_thread_fence(memory_order_seq_cst); - - int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state, - rel_timeout); - - atomic_fetch_sub_explicit(&rwlock->pending_readers, 1, memory_order_relaxed); - - if (ret == -ETIMEDOUT) { + if (abs_timeout_or_null != NULL) { + rel_timeout = &ts; + if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) { return ETIMEDOUT; } } + + rwlock->pending_lock.lock(); + rwlock->pending_reader_count++; + + // We rely on the fact that all atomic exchange operations on the same object (here it is + // rwlock->state) always appear to occur in a single total order. If the pending flag is added + // before unlocking, the unlocking thread will wakeup the waiter. Otherwise, we will see the + // state is unlocked and will not wait anymore. + old_state = atomic_fetch_or_explicit(&rwlock->state, STATE_HAVE_PENDING_READERS_FLAG, + memory_order_relaxed); + + int old_serial = rwlock->pending_reader_wakeup_serial; + rwlock->pending_lock.unlock(); + + int futex_ret = 0; + if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) { + futex_ret = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared, + old_serial, rel_timeout); + } + + rwlock->pending_lock.lock(); + rwlock->pending_reader_count--; + if (rwlock->pending_reader_count == 0) { + atomic_fetch_and_explicit(&rwlock->state, ~STATE_HAVE_PENDING_READERS_FLAG, + memory_order_relaxed); + } + rwlock->pending_lock.unlock(); + + if (futex_ret == -ETIMEDOUT) { + return ETIMEDOUT; + } } } +static inline __always_inline bool __can_acquire_write_lock(int old_state) { + return !__state_owned_by_readers_or_writer(old_state); +} + +static inline __always_inline int __pthread_rwlock_trywrlock(pthread_rwlock_internal_t* rwlock) { + int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); + + while (__predict_true(__can_acquire_write_lock(old_state))) { + if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, + __state_add_writer_flag(old_state), memory_order_acquire, memory_order_relaxed))) { + + atomic_store_explicit(&rwlock->writer_tid, __get_thread()->tid, memory_order_relaxed); + return 0; + } + } + return EBUSY; +} + static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock, const timespec* abs_timeout_or_null) { - if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id, - memory_order_relaxed))) { + if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) { return EDEADLK; } - while (true) { + int ret = __pthread_rwlock_trywrlock(rwlock); + if (ret == 0) { + return ret; + } + int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); - if (__predict_true(old_state == 0)) { - if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1, - memory_order_acquire, memory_order_relaxed)) { - // writer_thread_id is protected by rwlock and can only be modified in rwlock write - // owner thread. Other threads may read it for EDEADLK error checking, atomic operation - // is safe enough for it. - atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed); - return 0; - } - } else { - timespec ts; - timespec* rel_timeout = NULL; + if (__can_acquire_write_lock(old_state)) { + continue; + } - if (abs_timeout_or_null != NULL) { - rel_timeout = &ts; - if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) { - return ETIMEDOUT; - } - } + timespec ts; + timespec* rel_timeout = NULL; - // To avoid losing wake ups, the pending_writers increment should be observed before - // futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used - // here. Because only a seq_cst fence can ensure sequential consistency for non-atomic - // operations in futex_wait. - atomic_fetch_add_explicit(&rwlock->pending_writers, 1, memory_order_relaxed); - - atomic_thread_fence(memory_order_seq_cst); - - int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state, - rel_timeout); - - atomic_fetch_sub_explicit(&rwlock->pending_writers, 1, memory_order_relaxed); - - if (ret == -ETIMEDOUT) { + if (abs_timeout_or_null != NULL) { + rel_timeout = &ts; + if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) { return ETIMEDOUT; } } + + rwlock->pending_lock.lock(); + rwlock->pending_writer_count++; + + old_state = atomic_fetch_or_explicit(&rwlock->state, STATE_HAVE_PENDING_WRITERS_FLAG, + memory_order_relaxed); + + int old_serial = rwlock->pending_writer_wakeup_serial; + rwlock->pending_lock.unlock(); + + int futex_ret = 0; + if (!__can_acquire_write_lock(old_state)) { + futex_ret = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared, + old_serial, rel_timeout); + } + + rwlock->pending_lock.lock(); + rwlock->pending_writer_count--; + if (rwlock->pending_writer_count == 0) { + atomic_fetch_and_explicit(&rwlock->state, ~STATE_HAVE_PENDING_WRITERS_FLAG, + memory_order_relaxed); + } + rwlock->pending_lock.unlock(); + + if (futex_ret == -ETIMEDOUT) { + return ETIMEDOUT; + } } } int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_interface) { pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); - + // Avoid slowing down fast path of rdlock. + if (__predict_true(__pthread_rwlock_tryrdlock(rwlock) == 0)) { + return 0; + } return __pthread_rwlock_timedrdlock(rwlock, NULL); } @@ -261,19 +437,15 @@ int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespe } int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock_interface) { - pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); - - int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); - - while (old_state >= 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, - old_state + 1, memory_order_acquire, memory_order_relaxed)) { - } - return (old_state >= 0) ? 0 : EBUSY; + return __pthread_rwlock_tryrdlock(__get_internal_rwlock(rwlock_interface)); } int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) { pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); - + // Avoid slowing down fast path of wrlock. + if (__predict_true(__pthread_rwlock_trywrlock(rwlock) == 0)) { + return 0; + } return __pthread_rwlock_timedwrlock(rwlock, NULL); } @@ -284,65 +456,52 @@ int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespe } int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock_interface) { - pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); - - int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); - - while (old_state == 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1, - memory_order_acquire, memory_order_relaxed)) { - } - if (old_state == 0) { - atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed); - return 0; - } - return EBUSY; + return __pthread_rwlock_trywrlock(__get_internal_rwlock(rwlock_interface)); } - int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_interface) { pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface); int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); - if (__predict_false(old_state == 0)) { - return EPERM; - } else if (old_state == -1) { - if (atomic_load_explicit(&rwlock->writer_thread_id, memory_order_relaxed) != __get_thread()->tid) { + if (__state_owned_by_writer(old_state)) { + if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) != __get_thread()->tid) { return EPERM; } - // We're no longer the owner. - atomic_store_explicit(&rwlock->writer_thread_id, 0, memory_order_relaxed); - // Change state from -1 to 0. - atomic_store_explicit(&rwlock->state, 0, memory_order_release); - - } else { // old_state > 0 - // Reduce state by 1. - while (old_state > 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, - old_state - 1, memory_order_release, memory_order_relaxed)) { - } - - if (old_state <= 0) { - return EPERM; - } else if (old_state > 1) { + atomic_store_explicit(&rwlock->writer_tid, 0, memory_order_relaxed); + old_state = atomic_fetch_and_explicit(&rwlock->state, ~STATE_OWNED_BY_WRITER_FLAG, + memory_order_release); + if (!__state_have_pending_readers_or_writers(old_state)) { return 0; } - // old_state = 1, which means the last reader calling unlock. It has to wake up waiters. + + } else if (__state_owned_by_readers(old_state)) { + old_state = atomic_fetch_sub_explicit(&rwlock->state, STATE_READER_COUNT_CHANGE_STEP, + memory_order_release); + if (!__state_is_last_reader(old_state) || !__state_have_pending_readers_or_writers(old_state)) { + return 0; + } + + } else { + return EPERM; } - // If having waiters, wake up them. - // To avoid losing wake ups, the update of state should be observed before reading - // pending_readers/pending_writers by all threads. Use read locking as an example: - // read locking thread unlocking thread - // pending_readers++; state = 0; - // seq_cst fence seq_cst fence - // read state for futex_wait read pending_readers for futex_wake - // - // So when locking and unlocking threads are running in parallel, we will not get - // in a situation that the locking thread reads state as negative and needs to wait, - // while the unlocking thread reads pending_readers as zero and doesn't need to wake up waiters. - atomic_thread_fence(memory_order_seq_cst); - if (__predict_false(atomic_load_explicit(&rwlock->pending_readers, memory_order_relaxed) > 0 || - atomic_load_explicit(&rwlock->pending_writers, memory_order_relaxed) > 0)) { - __futex_wake_ex(&rwlock->state, rwlock->process_shared(), INT_MAX); + // Wake up pending readers or writers. + rwlock->pending_lock.lock(); + if (rwlock->pending_writer_count != 0) { + rwlock->pending_writer_wakeup_serial++; + rwlock->pending_lock.unlock(); + + __futex_wake_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared, 1); + + } else if (rwlock->pending_reader_count != 0) { + rwlock->pending_reader_wakeup_serial++; + rwlock->pending_lock.unlock(); + + __futex_wake_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared, INT_MAX); + + } else { + // It happens when waiters are woken up by timeout. + rwlock->pending_lock.unlock(); } return 0; } diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 83a56d6f4..26d68e494 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -85,6 +85,11 @@ typedef long pthread_rwlockattr_t; #define PTHREAD_RWLOCK_INITIALIZER { { 0 } } +enum { + PTHREAD_RWLOCK_PREFER_READER_NP = 0, + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 1, +}; + typedef int pthread_key_t; typedef int pthread_once_t; @@ -178,10 +183,12 @@ int pthread_mutex_unlock(pthread_mutex_t*) __nonnull((1)); int pthread_once(pthread_once_t*, void (*)(void)) __nonnull((1, 2)); +int pthread_rwlockattr_init(pthread_rwlockattr_t*) __nonnull((1)); int pthread_rwlockattr_destroy(pthread_rwlockattr_t*) __nonnull((1)); int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t*, int*) __nonnull((1, 2)); -int pthread_rwlockattr_init(pthread_rwlockattr_t*) __nonnull((1)); int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int) __nonnull((1)); +int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t*, int*) __nonnull((1, 2)); +int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t*, int) __nonnull((1)); int pthread_rwlock_destroy(pthread_rwlock_t*) __nonnull((1)); int pthread_rwlock_init(pthread_rwlock_t*, const pthread_rwlockattr_t*) __nonnull((1)); diff --git a/libc/private/bionic_lock.h b/libc/private/bionic_lock.h new file mode 100644 index 000000000..6a0fd06ad --- /dev/null +++ b/libc/private/bionic_lock.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef _BIONIC_LOCK_H +#define _BIONIC_LOCK_H + +#include +#include "private/bionic_futex.h" + +class Lock { + private: + enum LockState { + Unlocked = 0, + LockedWithoutWaiter, + LockedWithWaiter, + }; + _Atomic(LockState) state; + bool process_shared; + + public: + Lock(bool process_shared = false) { + init(process_shared); + } + + void init(bool process_shared) { + atomic_init(&state, Unlocked); + this->process_shared = process_shared; + } + + void lock() { + LockState old_state = Unlocked; + if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state, + LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed))) { + return; + } + while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) { + // TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping. + __futex_wait_ex(&state, process_shared, LockedWithWaiter, NULL); + } + return; + } + + void unlock() { + if (atomic_exchange_explicit(&state, Unlocked, memory_order_release) == LockedWithWaiter) { + __futex_wake_ex(&state, process_shared, 1); + } + } +}; + +#endif // _BIONIC_LOCK_H diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index f96ccf9d0..2d21e301d 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -660,6 +660,37 @@ TEST(pthread, pthread_attr_setstacksize) { #endif // __BIONIC__ } +TEST(pthread, pthread_rwlockattr_smoke) { + pthread_rwlockattr_t attr; + ASSERT_EQ(0, pthread_rwlockattr_init(&attr)); + + int pshared_value_array[] = {PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED}; + for (size_t i = 0; i < sizeof(pshared_value_array) / sizeof(pshared_value_array[0]); ++i) { + ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, pshared_value_array[i])); + int pshared; + ASSERT_EQ(0, pthread_rwlockattr_getpshared(&attr, &pshared)); + ASSERT_EQ(pshared_value_array[i], pshared); + } + + int kind_array[] = {PTHREAD_RWLOCK_PREFER_READER_NP, + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP}; + for (size_t i = 0; i < sizeof(kind_array) / sizeof(kind_array[0]); ++i) { + ASSERT_EQ(0, pthread_rwlockattr_setkind_np(&attr, kind_array[i])); + int kind; + ASSERT_EQ(0, pthread_rwlockattr_getkind_np(&attr, &kind)); + ASSERT_EQ(kind_array[i], kind); + } + + ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr)); +} + +TEST(pthread, pthread_rwlock_init_same_as_PTHREAD_RWLOCK_INITIALIZER) { + pthread_rwlock_t lock1 = PTHREAD_RWLOCK_INITIALIZER; + pthread_rwlock_t lock2; + ASSERT_EQ(0, pthread_rwlock_init(&lock2, NULL)); + ASSERT_EQ(0, memcmp(&lock1, &lock2, sizeof(lock1))); +} + TEST(pthread, pthread_rwlock_smoke) { pthread_rwlock_t l; ASSERT_EQ(0, pthread_rwlock_init(&l, NULL)); @@ -695,7 +726,6 @@ TEST(pthread, pthread_rwlock_smoke) { ASSERT_EQ(0, pthread_rwlock_wrlock(&l)); ASSERT_EQ(0, pthread_rwlock_unlock(&l)); -#ifdef __BIONIC__ // EDEADLK in "read after write" ASSERT_EQ(0, pthread_rwlock_wrlock(&l)); ASSERT_EQ(EDEADLK, pthread_rwlock_rdlock(&l)); @@ -705,7 +735,6 @@ TEST(pthread, pthread_rwlock_smoke) { ASSERT_EQ(0, pthread_rwlock_wrlock(&l)); ASSERT_EQ(EDEADLK, pthread_rwlock_wrlock(&l)); ASSERT_EQ(0, pthread_rwlock_unlock(&l)); -#endif ASSERT_EQ(0, pthread_rwlock_destroy(&l)); } @@ -807,6 +836,111 @@ TEST(pthread, pthread_rwlock_writer_wakeup_reader) { ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock)); } +class RwlockKindTestHelper { + private: + struct ThreadArg { + RwlockKindTestHelper* helper; + std::atomic& tid; + + ThreadArg(RwlockKindTestHelper* helper, std::atomic& tid) + : helper(helper), tid(tid) { } + }; + + public: + pthread_rwlock_t lock; + + public: + RwlockKindTestHelper(int kind_type) { + InitRwlock(kind_type); + } + + ~RwlockKindTestHelper() { + DestroyRwlock(); + } + + void CreateWriterThread(pthread_t& thread, std::atomic& tid) { + tid = 0; + ThreadArg* arg = new ThreadArg(this, tid); + ASSERT_EQ(0, pthread_create(&thread, NULL, + reinterpret_cast(WriterThreadFn), arg)); + } + + void CreateReaderThread(pthread_t& thread, std::atomic& tid) { + tid = 0; + ThreadArg* arg = new ThreadArg(this, tid); + ASSERT_EQ(0, pthread_create(&thread, NULL, + reinterpret_cast(ReaderThreadFn), arg)); + } + + private: + void InitRwlock(int kind_type) { + pthread_rwlockattr_t attr; + ASSERT_EQ(0, pthread_rwlockattr_init(&attr)); + ASSERT_EQ(0, pthread_rwlockattr_setkind_np(&attr, kind_type)); + ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr)); + ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr)); + } + + void DestroyRwlock() { + ASSERT_EQ(0, pthread_rwlock_destroy(&lock)); + } + + static void WriterThreadFn(ThreadArg* arg) { + arg->tid = gettid(); + + RwlockKindTestHelper* helper = arg->helper; + ASSERT_EQ(0, pthread_rwlock_wrlock(&helper->lock)); + ASSERT_EQ(0, pthread_rwlock_unlock(&helper->lock)); + delete arg; + } + + static void ReaderThreadFn(ThreadArg* arg) { + arg->tid = gettid(); + + RwlockKindTestHelper* helper = arg->helper; + ASSERT_EQ(0, pthread_rwlock_rdlock(&helper->lock)); + ASSERT_EQ(0, pthread_rwlock_unlock(&helper->lock)); + delete arg; + } +}; + +TEST(pthread, pthread_rwlock_kind_PTHREAD_RWLOCK_PREFER_READER_NP) { + RwlockKindTestHelper helper(PTHREAD_RWLOCK_PREFER_READER_NP); + ASSERT_EQ(0, pthread_rwlock_rdlock(&helper.lock)); + + pthread_t writer_thread; + std::atomic writer_tid; + helper.CreateWriterThread(writer_thread, writer_tid); + WaitUntilThreadSleep(writer_tid); + + pthread_t reader_thread; + std::atomic reader_tid; + helper.CreateReaderThread(reader_thread, reader_tid); + ASSERT_EQ(0, pthread_join(reader_thread, NULL)); + + ASSERT_EQ(0, pthread_rwlock_unlock(&helper.lock)); + ASSERT_EQ(0, pthread_join(writer_thread, NULL)); +} + +TEST(pthread, pthread_rwlock_kind_PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) { + RwlockKindTestHelper helper(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); + ASSERT_EQ(0, pthread_rwlock_rdlock(&helper.lock)); + + pthread_t writer_thread; + std::atomic writer_tid; + helper.CreateWriterThread(writer_thread, writer_tid); + WaitUntilThreadSleep(writer_tid); + + pthread_t reader_thread; + std::atomic reader_tid; + helper.CreateReaderThread(reader_thread, reader_tid); + WaitUntilThreadSleep(reader_tid); + + ASSERT_EQ(0, pthread_rwlock_unlock(&helper.lock)); + ASSERT_EQ(0, pthread_join(writer_thread, NULL)); + ASSERT_EQ(0, pthread_join(reader_thread, NULL)); +} + static int g_once_fn_call_count = 0; static void OnceFn() { ++g_once_fn_call_count; From 41efc92e35d1922e246230bac72da0054b3c6db3 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 2 Apr 2015 15:47:35 -0700 Subject: [PATCH 070/123] Use assembly memmove for all arm32 processors. Bug: 15110993 Change-Id: Ia3dcd6b8c4032f8c72b6f2e628b635ce99667c09 --- libc/arch-arm/cortex-a15/cortex-a15.mk | 8 +++++--- libc/arch-arm/cortex-a9/cortex-a9.mk | 8 +++++--- libc/arch-arm/krait/krait.mk | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libc/arch-arm/cortex-a15/cortex-a15.mk b/libc/arch-arm/cortex-a15/cortex-a15.mk index cc502cf29..6fa327022 100644 --- a/libc/arch-arm/cortex-a15/cortex-a15.mk +++ b/libc/arch-arm/cortex-a15/cortex-a15.mk @@ -1,5 +1,4 @@ libc_bionic_src_files_arm += \ - arch-arm/generic/bionic/memcmp.S \ arch-arm/cortex-a15/bionic/memcpy.S \ arch-arm/cortex-a15/bionic/memset.S \ arch-arm/cortex-a15/bionic/stpcpy.S \ @@ -10,5 +9,8 @@ libc_bionic_src_files_arm += \ arch-arm/cortex-a15/bionic/__strcpy_chk.S \ arch-arm/cortex-a15/bionic/strlen.S \ -libc_openbsd_src_files_arm += \ - upstream-openbsd/lib/libc/string/memmove.c \ +libc_bionic_src_files_arm += \ + arch-arm/generic/bionic/memcmp.S \ + +libc_bionic_src_files_arm += \ + arch-arm/denver/bionic/memmove.S \ diff --git a/libc/arch-arm/cortex-a9/cortex-a9.mk b/libc/arch-arm/cortex-a9/cortex-a9.mk index 7570567a8..7b38de15c 100644 --- a/libc/arch-arm/cortex-a9/cortex-a9.mk +++ b/libc/arch-arm/cortex-a9/cortex-a9.mk @@ -1,5 +1,4 @@ libc_bionic_src_files_arm += \ - arch-arm/generic/bionic/memcmp.S \ arch-arm/cortex-a9/bionic/memcpy.S \ arch-arm/cortex-a9/bionic/memset.S \ arch-arm/cortex-a9/bionic/stpcpy.S \ @@ -10,5 +9,8 @@ libc_bionic_src_files_arm += \ arch-arm/cortex-a9/bionic/__strcpy_chk.S \ arch-arm/cortex-a9/bionic/strlen.S \ -libc_openbsd_src_files_arm += \ - upstream-openbsd/lib/libc/string/memmove.c \ +libc_bionic_src_files_arm += \ + arch-arm/generic/bionic/memcmp.S \ + +libc_bionic_src_files_arm += \ + arch-arm/denver/bionic/memmove.S \ diff --git a/libc/arch-arm/krait/krait.mk b/libc/arch-arm/krait/krait.mk index 8bd5e8bff..88b4d663c 100644 --- a/libc/arch-arm/krait/krait.mk +++ b/libc/arch-arm/krait/krait.mk @@ -1,5 +1,4 @@ libc_bionic_src_files_arm += \ - arch-arm/generic/bionic/memcmp.S \ arch-arm/krait/bionic/memcpy.S \ arch-arm/krait/bionic/memset.S \ arch-arm/krait/bionic/strcmp.S \ @@ -13,5 +12,8 @@ libc_bionic_src_files_arm += \ arch-arm/cortex-a15/bionic/strcpy.S \ arch-arm/cortex-a15/bionic/strlen.S \ -libc_openbsd_src_files_arm += \ - upstream-openbsd/lib/libc/string/memmove.c \ +libc_bionic_src_files_arm += \ + arch-arm/generic/bionic/memcmp.S \ + +libc_bionic_src_files_arm += \ + arch-arm/denver/bionic/memmove.S \ From 4d3abcb033fcf84d1f7d04630273c1a2f7e51a4f Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Thu, 9 Apr 2015 09:22:25 +0100 Subject: [PATCH 071/123] Update update-tzdata.py tool to generate ICU4J data jars The ICU4J changes are not necessary for use on Android (since we use the ICU4C .dat file), but updating them ensures that the .jars in sync with everything else and the jars are currently required for host tests. Change-Id: Ie56b31af87e8fbd27a6489af8287e4b6a7be6b8f --- libc/tools/zoneinfo/update-tzdata.py | 31 +++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/libc/tools/zoneinfo/update-tzdata.py b/libc/tools/zoneinfo/update-tzdata.py index d5788afa2..68a5ff51c 100755 --- a/libc/tools/zoneinfo/update-tzdata.py +++ b/libc/tools/zoneinfo/update-tzdata.py @@ -33,9 +33,12 @@ CheckDirExists(bionic_libc_zoneinfo_dir, 'bionic/libc/zoneinfo') CheckDirExists(bionic_libc_tools_zoneinfo_dir, 'bionic/libc/tools/zoneinfo') print 'Found bionic in %s ...' % bionic_dir -# Find the icu4c directory. -icu_dir = os.path.realpath('%s/../external/icu/icu4c/source' % bionic_dir) -CheckDirExists(icu_dir, 'external/icu/icu4c/source') +# Find the icu directory. +icu_dir = os.path.realpath('%s/../external/icu' % bionic_dir) +icu4c_dir = os.path.realpath('%s/icu4c/source' % icu_dir) +icu4j_dir = os.path.realpath('%s/icu4j' % icu_dir) +CheckDirExists(icu4c_dir, 'external/icu/icu4c/source') +CheckDirExists(icu4j_dir, 'external/icu/icu4j') print 'Found icu in %s ...' % icu_dir @@ -116,14 +119,14 @@ def BuildIcuToolsAndData(data_filename): # Build the ICU tools. print 'Configuring ICU tools...' - subprocess.check_call(['%s/runConfigureICU' % icu_dir, 'Linux']) + subprocess.check_call(['%s/runConfigureICU' % icu4c_dir, 'Linux']) # Run the ICU tools. os.chdir('tools/tzcode') # The tz2icu tool only picks up icuregions and icuzones in they are in the CWD for icu_data_file in [ 'icuregions', 'icuzones']: - icu_data_file_source = '%s/tools/tzcode/%s' % (icu_dir, icu_data_file) + icu_data_file_source = '%s/tools/tzcode/%s' % (icu4c_dir, icu_data_file) icu_data_file_symlink = './%s' % icu_data_file os.symlink(icu_data_file_source, icu_data_file_symlink) @@ -134,7 +137,7 @@ def BuildIcuToolsAndData(data_filename): subprocess.check_call(['make']) # Copy the source file to its ultimate destination. - icu_txt_data_dir = '%s/data/misc' % icu_dir + icu_txt_data_dir = '%s/data/misc' % icu4c_dir print 'Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir shutil.copy('zoneinfo64.txt', icu_txt_data_dir) @@ -143,7 +146,7 @@ def BuildIcuToolsAndData(data_filename): subprocess.check_call(['make', 'INCLUDE_UNI_CORE_DATA=1', '-j32']) # Copy the .dat file to its ultimate destination. - icu_dat_data_dir = '%s/stubdata' % icu_dir + icu_dat_data_dir = '%s/stubdata' % icu4c_dir datfiles = glob.glob('data/out/tmp/icudt??l.dat') if len(datfiles) != 1: print 'ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles) @@ -152,6 +155,20 @@ def BuildIcuToolsAndData(data_filename): print 'Copying %s to %s ...' % (datfile, icu_dat_data_dir) shutil.copy(datfile, icu_dat_data_dir) + # Generate the ICU4J .jar files + os.chdir('%s/data' % icu_working_dir) + subprocess.check_call(['make', 'icu4j-data']) + + # Copy the ICU4J .jar files to their ultimate destination. + icu_jar_data_dir = '%s/main/shared/data' % icu4j_dir + jarfiles = glob.glob('out/icu4j/*.jar') + if len(jarfiles) != 2: + print 'ERROR: Unexpectedly found %d .jar files (%s). Halting.' % (len(jarfiles), jarfiles) + sys.exit(1) + for jarfile in jarfiles: + print 'Copying %s to %s ...' % (jarfile, icu_jar_data_dir) + shutil.copy(jarfile, icu_jar_data_dir) + # Switch back to the original working cwd. os.chdir(original_working_dir) From aba687a09cc3a5014f692e8f215e136da01dfd5d Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Wed, 8 Apr 2015 18:26:22 +0100 Subject: [PATCH 072/123] Upgrade timezone data to 2015b Changes affecting future time stamps Mongolia will start observing DST again this year, from the last Saturday in March at 02:00 to the last Saturday in September at 00:00. (Thanks to Ganbold Tsagaankhuu.) Palestine will start DST on March 28, not March 27. Also, correct the fall 2014 transition from September 26 to October 24. Adjust future predictions accordingly. (Thanks to Steffen Thorsen.) Changes affecting past time stamps The 1982 zone shift in Pacific/Easter has been corrected, fixing a 2015a regression. (Thanks to Stuart Bishop for reporting the problem.) Some more zones have been turned into links, when they differed from existing zones only for older time stamps. As usual, these changes affect UTC offsets in pre-1970 time stamps only. Their old contents have been moved to the 'backzone' file. The affected zones are: America/Antigua, America/Cayman, Pacific/Midway, and Pacific/Saipan. Changes affecting time zone abbreviations Correct the 1992-2010 DST abbreviation in Volgograd from "MSK" to "MSD". (Thanks to Hank W.) Bug: 19887183 Change-Id: I1b4bdc5ae5cf778908a77893d7f8db8a4117e1e1 --- libc/zoneinfo/tzdata | Bin 494904 -> 496043 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata index b9a66210eb8eb2a94d5db930cb3ac727921251b8..3661b68309f8dc5da60aaa5fdcdb16d787cd5608 100644 GIT binary patch delta 7340 zcmeI#eN!51f`$r)GA4THgu}4GcT#*yOw>_9QBX!iKt&xD6cuGOPoZ%N z`Fb2Q^RSLw50%xFD3oOA@r8;!lc`y0jcH~@r>U@I#jX4O?YS-Qy0^dXANQ=g*4Zrh z@a+A}-uwAJFMChp-hR&==oenJA$MWXLe&~SBuB?BT&G)W(*;a2ycE8pt5Q4=wO!)_ zC=cz!m97?L%998RO3>?(9F)*$kWOcuj?kc9cp@m`zm!e}g)4K4aV1DqGEX5iIHA)3 z)|{`^To4>?%5}21Q7cvjhbsj?;ZU%uEPoi@VAZs0lUCOq91d%@UZ&y?SX{5w&FL1d zY?y`8ZmP2RINaoFBW`p{&<7(tMD8gyBu}YGMtz8?RF1|ivO5=%p{n9~0XDLy4co}m zFQAd^KZ~}|1pR|BcTdnqBaMIcDDSQ+&n-t2IlL1-ax4eYJyhlRHe~cj&<{cd3$GWU zxktP~$&Eofi_I3q+@mVzdLru{IY}i8Z2u|p?hm*_E+!&2OjRx!U?;roUF4$;Xd%~z zqJw-s8P=Yv^3_b_lJDO}HDM35_KY{^e#x)ur7D5PkVv}DMFF|l3;W2FT6jp%L>POk zN=OKXliq(uA?bG$HROS8Tp&DIV7NvSv8KX7;vJ|ZYJXfN3H=cip(@E+kQ||!Cb#Ky z`bKyo!cY;>$26r@r!%e6ip>#yl$kwog>^HX2<@Yqa_-mZ0cdMv3oj`hhOm3JJ5M2xgrk(~sDhhRZN&}3aT6Y?D!UdTl|20joTTm{ z)DvF#7Ga8!eO2Y9#juf=M`If~{015cKhQ>4#oUj5*NQZ9;$@VRzvZKeFby9$eFD)@ zs&b|d8Km_-RFGUJn#p-9+R2A{#F$mZdjMJFdO9k}=X-F9_@Z!!bl4HwUsd!Uz)reY zv5W8uEu{BpbP)3aSnpF6OAd0$(5FyM#=L=6G7eP(n8&%Q0evxJKu;yTGYYt4!369h z9AzHieHf!TO#(2StQd?!va$#@gqONNb{|IIK$cD*iLCnt4pMRhwPZ_QTqfmv5Hv_t z9$$oH^5ifS5q_?Y$ebm6h9LBJs`8giOdz$dql6slf!AI`3Av#u7qLYz99{2|;NURUd#9~Cd@IGP&t4iW)$RhX@m1M|9 zoFXG{oC1Mvzei1F?!`cKP zsH$oGRZgV=s8cOU*B5b_2S`wYsyKB>COej+h-_JgI)CX3dHj8ZCaTIGb(kPLjgmyw z^yIfXU7!!{M2k}W3A{YOGldONO?wCHbe)5cI>e$J7=h9u?DZPBdEl90I$fs^af=6D z?1Av1ywkHtC5NxWNsf#{JvrJBx5)9Uh#bb%aR+SVR0Ot>2d<-$oZgQ%Kjz^aBe_T; zY-l-YeH=~Xok{SK^T!Z9!k>1=2#%F{RQPEo@0~z9xw08CBROQskww_bO7hWrI7Mzg zgFEEQjffq^2|5sV(tZuQ2vct%KSZH}{8$calGZV~q$3U0NxZJkiB?hnNY#U?VyHqg5q10mDhR(V=@G9Gpv$JXH<3>gA;SUaC&a zU+*ZwJF{516An&IRHmQ86;`}?38B+8CXmlkQ6jvFL*!N}yuvwzP3J?$h7{o(O34?g za0_qZ2Kh1t;WlyxsibWJoWe=e6JFsK`6?NaGsx?(k$;TCHsLfH$=4}p6W)S(CTGnw zqzP}MoP3jtCgCmk$hQw8dKP&D8RWYZR0wC$On9MoKQU>XbPpj*XhNl*Q-1D{+bUwy z;|-m3F4)smB_IvE$nH@f?Q&L2Gu0?ezcOrrm8ud2yI9tqXSSt zCau6e!dN|IKG)j}?UcjGl0p>vOErY6*ad$na4rXX0g}j;EI7#aHK--Lhs%U_6f{p& z{w)s4#Qi#o$g5t|5uWo3Io%bZnX2;UWK19z8&N{I7#$*iwl#q}LHdF5r9JSFn*mGq8!HaXKY0^S=9}#i}I9JPa`o`RaTEgf$$#oku}@l$<0%WN-%t}wwM+Y zE~Yh$)sU*Kd`rD1xA5>n$HEo<`{jLbSq;o%U+qFtUS8MYo}7k5rPfsVKJE=`aSvtF z6kKMFO(zhP&pYjfWWra#BEr|aI>L8_D`bBzLJQR3uV(T&xlvAHn>pTpA(O$#r$9-8 zsyy0&L*&Wl;U)ZF*b=^o<|Bph^QGkAEV!4bCjaVzTT3j;^J5XdR8?N=j#S|)oWxy@ zdQyJ~x5$wZh+IZ~hK(G38rw+2C^V8r1#N!Jg)DU-jl9x=a>9l;kz@Jrk)|GqUap#& zr}5^0MAmYP(!>Yra=yIPqL~~oL_2x?KE$lxH=1e4A}3N%DO|uQa`F$j<0p0{SJ$4f z3;Zs#QZ+S<;(5=ab)`i<+ra~;ufXcyVp@t^ayAIngaNdYmWfrX`1c|b3HKEc4#a(g zv(K|i4Zf#Br!$x&>6z1~{Abc#jh)%m3g1;Y+PaU9t{P*KM{nV3Z9dl!E;tuf@0E9B z#~STxSuNogq|1cw-$6xMV#$QxA&LmUX4MgX>AFJr=5BB_ZF*N-r%?9fEn5idD3L~2N>I%SX_&-8}EgCZO_A{Yag9*_|!=hx!j6A=&`Wj zdj2X6VZ#jvUU7xa)d#tnJL>~oZ+&BUtt`Fg-kRB+7CL6%F0GmK>3B!R#RWBU&t?1O z9p7lqd||9FYyW)n{3izZ7Hl44&R)^UXJ63Yym0DgzMRnp^P<6T_;UMxYF^yyrXw#v zUz7KJizEN$^_qf9&pMWzIbE~#XoX|h!N1lN?q1MIUOH5dQV!?*Tpi+SBg?Y@$~hnm;7ba!lMjIG)D*Y6yq)!k|~ zJ$BKtd7ZDOEWg2_S6uPO^s%mihJZNNxpsqD*<9)RqTLXKknbYywz6T+cZT))GKou? zDoS%T*s$DEW)h z2cqeIxq&hwI_5Kdz)>`u_j3t0Hx}F5^?Crwty?zq8U}rzTj2 zScb+<9U-_@sro)zM>afo+u-b8Ho4H2-t)Y7_Wjdrvv1dXGdf#s86Ul4pZjg9XWsF{ z_RRAZPuBiF+2_9;>RGUPojrT!w;ua~x%P#tE_iZAkFqbCanzIhyE)#)Bj2^<1&r|K z_c>zA|Lk6G!H+K6k~9DAUHZX#+p>cndkbI9v@L(K!@lCcI?u|LAK4v`%=N6A-fSPW zdeJD)nz+B%i^li$6o+iHuf6XV&)To^>?O)IPs!!2-t}MpY}?S7>)rU4*H&8nkayE_ zCvBTcV!UOK*ScQmZrlmymaeWjJ&bc33ImP(4ALG6OWGt#yENH0QQD`;Hj2_tQQ9g> zdqrupDD4)d?V_|_lng}4L6j`SG6_Wfkx)blMwD`p74vv2Qk&-rwQGvi{Rd+xavobKDLnJ?Hl z&6io{KcRAZPqk*R2;;Plvo4(aJ*RVb>!QJzb-8`Nvo7w{?#v4aap!%1$(eui8+XB_ zM(2_<@4A;B-RoR-@QAx`cd2vvqb~P~vOS!Y^;SprM$X3F*41O?kK`;Yu@*&*;q04d zE&ioH9|lR*bsrn}5a?%J-xB5A&^Xe)u}<$St&VhWdhEJ$^SYniW%;L_de^F*`b^in z{>C_0Wt4G%KC7WV%2@L2Gq=tyQ%_o^$<~R|LQz^NN=s#v){4?%*`(E?v|Ki6y(kID zCMk%Lglv+A$V9Yfz7~t@kZ44SN0SkW5|b!Vi4vD6k%`{UZQQ$Jn0 zW#~OA(`Wzo)7ADAUk2?5jGY@^>u>yOEyv<>)!JD6r@j=9{NKM6`p4p)J;s?FhHZQ9 z4#T}Q64Jk3{r*p0{q7DzgW?~C?2))WUD8v>@?DRAe{(cG=6%Y@-%btQr%XA~`u+{$ zn@tsZeP5U7gelH2b=Cye&nHX|bN}R%rb^|%?T>QBj5PheVa90_X6SQVpY{s)LqmRF zLsz|PeRx0=x<5D0^F5(n6zP*LGa9m+vR0mU3;P$Wz=P#{!FDl0Pd zAYW>8x@7oriWCcz8cZxqELMk3b4!Z~buu!pS%Ai%n!Fq1U1a*W<7TCj@-O z$R%3j(qNlu#S9iVYQ@@Mn^<`S&B2OTV}UPNF|EB_i){(EA#PZhsjPv;^;+z*VK%Wb z6cxi1v8fU*jW_6ZpVfvbBFK(JV(CT!`T9vTko3{;lHfKNM=Qem2$D(o zNEDLDMQ9{3AL0aIWq~%06cYayiiq+h_K<`+^pM0lSi%)C{Ss2c71Qh-olf5dUw8? zQ!_x`O+?hSis-rq+2sAFQB79NKnM9~H!hLh5=2MxriH;p`npg3~|>fBK9QmNb)e$lj$kwCbLYn6PTRowG+l--h@!G zpaKOvkmErES@Cc1l6(h@QCu?{kWB7xL?J2q8jXZaognp-5IB)JPeck?zZOMg!$RyK zkJO-tY_Y&HiK+h>sibBvN(irOB60zfXPaQXUJ<+hidp0Z1?6P_gJ>qNIN&3mu?UIQ zW^NjJvkn!6ooFHNHlfd7vfZE^boi;fK?y3W=iTD8;Vr9NY!1cuQK91GY4kIqXBZ-G zRK%xYaFR1RY$lxWZDimTTp(;9aT)gEs!{QN57VtMp!RyKi(PtDOy#sFKnqX2ILNkVpr0r9O^0ooBJNuP2Wc6JN^;;n zw31iT(N7Lr5RpU%A=@HVY^pfLAUhhS8H!oWIhsaW$Bl>C}7cGK?4am054(s zjJI)eE<-X2x`aY9{4g5H$fGzxLR|>Fo!dhjQV0`OL?W-p9zQ*j^RT39V@gd`OxGo8 zDKAg8|Ms;x)h@Hx%a}=Fuuj*8F^fzNMLCK65X~fU8+_!Z*AX&95x4yrX~Z!Z6(r+( zw2;Lv^bwZ>wwW3Z!r5I(mM=#uDLR9GQu+WQX61`Afz7iN@!B_NBOBhq1@h<`MBbr@ z$85+XJXb}w_Mx3TvmOIPeF0H-D&kM;kWKa!pqlI|M~A<3iR{~s=s#$ey9lqVA+3|p zN!Y>_!uDd)v=BF8Wp#wtbrD8hCG14pUE0=`N4U|}6K-GKr1P`dySZ&uB9XlFItmD% zgAIiHh?j7GG|tvGp5)ou!x)7ua%*cO+<;CH#soUF4Jd_hpDc1H!Hvay3Kq&uW@hO! z%u9&eo@z4a`E>1J%m>Y|r1PCXhtzas_$OstbX;FL-5vL?FDgOtG6o0Wh4e&m*FETE zpsx_tIU2JhXHibRaHCmr7Cv$|46xx`};$?Luy*8<0qno+B_O z?u2zQUp&rW7WrTf$_Y24X2QkgBd1Ow#HH1lMmV4faxn@mu6RShPsh>6V!(LVmiUW~ zC0cAH33?2z{!%}&&PK#iMc5vNlU(~XHk0Ulv=JuZ0*Tv#$Q&&*nK}O2s#r{Z5$$C9 z-5AJGOn1Gj(*=x1wA(J`oJO`=Ypt3r%0h=*vE**xJ71ohvwxTYZNi!j0n9{nu9grN z;hnC@Rf2qS5pagZg|?X%5^koAE0p25%ooHjWEbwv zEy`Wv?=SnoZ8b2T_bLu4`T4=qK+{!TV%w?pl)GDhW&wqiIm5Rx~nV^5AUA%A#w)+&(v>y)g(t1>qy-{c-FQ=hj zauN}%xW_#UCuz9}nXs5MWvPTeJxU0Foi!2uR_i7FnPDweg5Qw4_fN8!tjzzp=iJrGOSR3d znQ+znN|m6_zj5KrlcDft%H?1-em9?`GECoi4O+@V4_1^tK3lCxHV+w(q=bRq8NE)u z7#w`Zs?!Y)9%|7U2M6En&;>fuGj(^U_O-@aC(E!w{`)^eGctA4GcwihgY|I-`Epev zn;MPbt%Lf(!8ZMGbozwLE6P}&Z47(3$hXP_bFq4`!VCWEf@$`5RqYqGTyl9A2ofi#Jcz)mL--UU0 zTr^bZ|Ccyp(9E>C>WIgT*QkyCdW&b>4~EJSo1z|ZW`#WLUSP~~F8KL5-@?8-o!O^0 z`4)9XJMTG?>s!1h$mx1+rfj9^WJ55M!A>AbbIqAncOQZ zFMIO?`rY}L|Khy&tM}XmC-*y79&dKv_u3zwtNy&rUHEjdbM>~pzBQ#8&imI?`--xr zI*aG5@!hiawt3!lcBijoT!OcBnBpt_CDdE?<6QRxr+)O7e-iIrf8#^(-u zE1tRRe(>=g=R;+mxHsj$p$?2RZb#+AL)C4gjEg+Gh8V{gb(NdXg zsZ6+3CS58MFO|uc${Qe+H$lo9;pA-l^}kwcGd`hq4l^z>C&ns?G3hs{mEp#ir+vmi z&ywNBPsKm;g!6F1O*H;%iv9mjv2(x8E)QqdjV8Ta-FL0ALw&edgdwuQrfwQ*Z;%OSVIsu!MS@35zjb{p`L9Mjo+5>>WYTnU+vtXUA=q9KU~eDvBsb*$8>EE zmHYk^IlGMf3-ILZGNni9$9nd^W2)Bc!_}L+OtI?fF4J8+=IJukh~FI>r=FQ-8mD@v z87!V-e=%X6e!04Etobp+z=Sx@=VQ&2_3FkA!lDM-4M}R+wdPg&JWuPj=3u=R&>m?{ z7Kqr8o+fd|3c(*>Y=2?X;N9UCTo|<7UY80S`m5!t6tP5 zn_&a|&c=RiMmu7a4_1GJ>VrvU1s%^>RI5!N=!s1*hYV38oAey$JcDvse1iv>2CE)*!F&*CIQW6eY`5 kd*mnUG-`GFKecDAzh0(5r>5G?H>hrpA=HzdV$R6=I|m6$(f|Me From c0e7dbb1dbe92c9e72bdbd299fa50786fd751807 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 9 Apr 2015 13:58:53 -0700 Subject: [PATCH 073/123] Add Elfxx_Ver* types Bug: http://b/20139821 Change-Id: I7a367b08faa3bf5c005996c066cd35709f533265 --- libc/include/elf.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/libc/include/elf.h b/libc/include/elf.h index ee53ad14b..801d9ff72 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -48,6 +48,71 @@ typedef struct { } a_un; } Elf64_auxv_t; +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + +typedef struct { + Elf32_Half vd_version; + Elf32_Half vd_flags; + Elf32_Half vd_ndx; + Elf32_Half vd_cnt; + Elf32_Word vd_hash; + Elf32_Word vd_aux; + Elf32_Word vd_next; +} Elf32_Verdef; + +typedef struct { + Elf32_Word vda_name; + Elf32_Word vda_next; +} Elf32_Verdaux; + +typedef struct { + Elf64_Half vd_version; + Elf64_Half vd_flags; + Elf64_Half vd_ndx; + Elf64_Half vd_cnt; + Elf64_Word vd_hash; + Elf64_Word vd_aux; + Elf64_Word vd_next; +} Elf64_Verdef; + +typedef struct { + Elf64_Word vda_name; + Elf64_Word vda_next; +} Elf64_Verdaux; + +typedef struct { + Elf32_Half vn_version; + Elf32_Half vn_cnt; + Elf32_Word vn_file; + Elf32_Word vn_aux; + Elf32_Word vn_next; +} Elf32_Verneed; + +typedef struct { + Elf32_Word vna_hash; + Elf32_Half vna_flags; + Elf32_Half vna_other; + Elf32_Word vna_name; + Elf32_Word vna_next; +} Elf32_Vernaux; + +typedef struct { + Elf64_Half vn_version; + Elf64_Half vn_cnt; + Elf64_Word vn_file; + Elf64_Word vn_aux; + Elf64_Word vn_next; +} Elf64_Verneed; + +typedef struct { + Elf64_Word vna_hash; + Elf64_Half vna_flags; + Elf64_Half vna_other; + Elf64_Word vna_name; + Elf64_Word vna_next; +} Elf64_Vernaux; + #define DF_ORIGIN 0x00000001 #define DF_SYMBOLIC 0x00000002 #define DF_TEXTREL 0x00000004 From ef25592f14d23ce6294ea103e9edf894779d141d Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 8 Apr 2015 11:53:08 -0700 Subject: [PATCH 074/123] Fix dl* tests to run-on-host Bug: http://b/20121152 Change-Id: I1e1f41d885c75dbb26f91565a53a15d62ef72ce6 --- tests/dlext_test.cpp | 6 +++++- tests/dlfcn_test.cpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 70124180d..56a8f6f1f 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -134,6 +134,10 @@ TEST_F(DlExtTest, ExtInfoUseFdWithOffset) { TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) { const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH; + // lib_path is relative when $ANDROID_DATA is relative + char lib_realpath_buf[PATH_MAX]; + ASSERT_TRUE(realpath(lib_path.c_str(), lib_realpath_buf) == lib_realpath_buf); + const std::string lib_realpath = std::string(lib_realpath_buf); android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; @@ -158,7 +162,7 @@ TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) { extinfo.library_fd_offset = PAGE_SIZE; handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo); ASSERT_TRUE(handle_ == nullptr); - ASSERT_EQ("dlopen failed: \"" + lib_path + "\" has bad ELF magic", dlerror()); + ASSERT_EQ("dlopen failed: \"" + lib_realpath + "\" has bad ELF magic", dlerror()); close(extinfo.library_fd); } diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 1d6242836..708e2cd5f 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -764,7 +764,11 @@ TEST(dlfcn, dladdr_libc) { void* addr = reinterpret_cast(puts); // well-known libc function ASSERT_TRUE(dladdr(addr, &info) != 0); - ASSERT_STREQ(BIONIC_PATH_TO_LIBC, info.dli_fname); + // /system/lib is symlink when this test is executed on host. + char libc_realpath[PATH_MAX]; + ASSERT_TRUE(realpath(BIONIC_PATH_TO_LIBC, libc_realpath) == libc_realpath); + + ASSERT_STREQ(libc_realpath, info.dli_fname); // TODO: add check for dfi_fbase ASSERT_STREQ("puts", info.dli_sname); ASSERT_EQ(addr, info.dli_saddr); From d032378790c787b8e03cebff92619b41ab0dffe4 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 9 Apr 2015 17:18:53 -0700 Subject: [PATCH 075/123] Don't build any changes that touch bionicbb. Right now any changes in here would be innocuous because I manually update bionicbb, but I'd like to check in the various job configurations. Once I have we don't want anyone to be able to make the buildbot run any untrusted code. Change-Id: Ic050859cd5017615f71c75f995ba21bb45407b05 --- tools/bionicbb/gmail_listener.py | 6 ++++++ tools/bionicbb/test_gmail_listener.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py index 3e501cca9..632426b8e 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/gmail_listener.py @@ -64,6 +64,11 @@ def contains_cleanspec(change_id, patch_set): return 'CleanSpec.mk' in [os.path.basename(f) for f in files] +def contains_bionicbb(change_id, patch_set): + files = gerrit.get_files_for_revision(change_id, patch_set) + return any('tools/bionicbb' in f for f in files) + + def should_skip_build(info): if info['MessageType'] not in ('newchange', 'newpatchset', 'comment'): raise ValueError('should_skip_build() is only valid for new ' @@ -75,6 +80,7 @@ def should_skip_build(info): checks = [ is_untrusted_committer, contains_cleanspec, + contains_bionicbb, ] for check in checks: if check(change_id, patch_set): diff --git a/tools/bionicbb/test_gmail_listener.py b/tools/bionicbb/test_gmail_listener.py index af9eda094..f8b9ab602 100644 --- a/tools/bionicbb/test_gmail_listener.py +++ b/tools/bionicbb/test_gmail_listener.py @@ -4,6 +4,7 @@ import unittest class TestShouldSkipBuild(unittest.TestCase): + @mock.patch('gmail_listener.contains_bionicbb') @mock.patch('gmail_listener.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_accepts_googlers(self, mock_commit, *other_checks): @@ -21,6 +22,7 @@ class TestShouldSkipBuild(unittest.TestCase): 'PatchSet': '', })) + @mock.patch('gmail_listener.contains_bionicbb') @mock.patch('gmail_listener.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_rejects_googlish_domains(self, mock_commit, *other_checks): @@ -38,6 +40,7 @@ class TestShouldSkipBuild(unittest.TestCase): 'PatchSet': '', })) + @mock.patch('gmail_listener.contains_bionicbb') @mock.patch('gmail_listener.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_rejects_non_googlers(self, mock_commit, *other_checks): @@ -55,6 +58,7 @@ class TestShouldSkipBuild(unittest.TestCase): 'PatchSet': '', })) + @mock.patch('gmail_listener.contains_bionicbb') @mock.patch('gmail_listener.is_untrusted_committer') @mock.patch('gerrit.get_files_for_revision') def test_skips_cleanspecs(self, mock_files, *other_checks): @@ -69,6 +73,21 @@ class TestShouldSkipBuild(unittest.TestCase): 'PatchSet': '', })) + @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('gmail_listener.is_untrusted_committer') + @mock.patch('gerrit.get_files_for_revision') + def test_skips_bionicbb(self, mock_files, *other_checks): + mock_files.return_value = ['tools/bionicbb/common.sh'] + for other_check in other_checks: + other_check.return_value = False + + for message_type in ('newchange', 'newpatchset', 'comment'): + self.assertTrue(gmail_listener.should_skip_build({ + 'MessageType': message_type, + 'Change-Id': '', + 'PatchSet': '', + })) + if __name__ == '__main__': unittest.main() From a0ecd5b2b4bcb2979f0105c444fa48ef4a66ea8e Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 13 Apr 2015 17:32:19 -0700 Subject: [PATCH 076/123] Skip merge-failed messages from Gerrit. Change-Id: I2d8055a44cd78f95e64d6cf88e9efdd610a4fa88 --- tools/bionicbb/gmail_listener.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py index 632426b8e..dd0c008b1 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/gmail_listener.py @@ -316,6 +316,7 @@ def skip_handler(gerrit_info, _, __): gerrit_info['Change-Id']) return True handle_abandon = skip_handler +handle_merge_failed = skip_handler handle_merged = skip_handler handle_restore = skip_handler handle_revert = skip_handler @@ -329,7 +330,8 @@ def process_message(msg, dry_run): print termcolor.colored('No info found: {}'.format(msg['id']), 'red') msg_type = gerrit_info['MessageType'] - handler = 'handle_{}'.format(gerrit_info['MessageType']) + handler = 'handle_{}'.format( + gerrit_info['MessageType'].replace('-', '_')) if handler in globals(): return globals()[handler](gerrit_info, body, dry_run) else: From 5ddbb3f936ee44555a46020239e49ab45109a806 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 5 Mar 2015 20:35:32 -0800 Subject: [PATCH 077/123] Prevent using static-allocated pthread keys before creation. Bug: 19993460 Change-Id: I244dea7f5df3c8384f88aa48d635348fafc9cbaf --- libc/bionic/pthread_key.cpp | 24 ++++++++++++++++++------ tests/pthread_test.cpp | 13 +++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/libc/bionic/pthread_key.cpp b/libc/bionic/pthread_key.cpp index 65e087902..6d77afac6 100644 --- a/libc/bionic/pthread_key.cpp +++ b/libc/bionic/pthread_key.cpp @@ -57,8 +57,15 @@ static inline bool SeqOfKeyInUse(uintptr_t seq) { return seq & (1 << SEQ_KEY_IN_USE_BIT); } +#define KEY_VALID_FLAG (1 << 31) + +static_assert(sizeof(pthread_key_t) == sizeof(int) && static_cast(-1) < 0, + "pthread_key_t should be typedef to int"); + static inline bool KeyInValidRange(pthread_key_t key) { - return key >= 0 && key < BIONIC_PTHREAD_KEY_COUNT; + // key < 0 means bit 31 is set. + // Then key < (2^31 | BIONIC_PTHREAD_KEY_COUNT) means the index part of key < BIONIC_PTHREAD_KEY_COUNT. + return (key < (KEY_VALID_FLAG | BIONIC_PTHREAD_KEY_COUNT)); } // Called from pthread_exit() to remove all pthread keys. This must call the destructor of @@ -114,7 +121,7 @@ int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) { while (!SeqOfKeyInUse(seq)) { if (atomic_compare_exchange_weak(&key_map[i].seq, &seq, seq + SEQ_INCREMENT_STEP)) { atomic_store(&key_map[i].key_destructor, reinterpret_cast(key_destructor)); - *key = i; + *key = i | KEY_VALID_FLAG; return 0; } } @@ -127,9 +134,10 @@ int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) { // responsibility of the caller to properly dispose of the corresponding data // and resources, using any means it finds suitable. int pthread_key_delete(pthread_key_t key) { - if (!KeyInValidRange(key)) { + if (__predict_false(!KeyInValidRange(key))) { return EINVAL; } + key &= ~KEY_VALID_FLAG; // Increase seq to invalidate values in all threads. uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed); if (SeqOfKeyInUse(seq)) { @@ -141,9 +149,10 @@ int pthread_key_delete(pthread_key_t key) { } void* pthread_getspecific(pthread_key_t key) { - if (!KeyInValidRange(key)) { + if (__predict_false(!KeyInValidRange(key))) { return NULL; } + key &= ~KEY_VALID_FLAG; uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed); pthread_key_data_t* data = &(__get_thread()->key_data[key]); // It is user's responsibility to synchornize between the creation and use of pthread keys, @@ -151,16 +160,19 @@ void* pthread_getspecific(pthread_key_t key) { if (__predict_true(SeqOfKeyInUse(seq) && data->seq == seq)) { return data->data; } + // We arrive here when current thread holds the seq of an deleted pthread key. So the + // data is for the deleted pthread key, and should be cleared. data->data = NULL; return NULL; } int pthread_setspecific(pthread_key_t key, const void* ptr) { - if (!KeyInValidRange(key)) { + if (__predict_false(!KeyInValidRange(key))) { return EINVAL; } + key &= ~KEY_VALID_FLAG; uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed); - if (SeqOfKeyInUse(seq)) { + if (__predict_true(SeqOfKeyInUse(seq))) { pthread_key_data_t* data = &(__get_thread()->key_data[key]); data->seq = seq; data->data = const_cast(ptr); diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index f96ccf9d0..6dbd964d1 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -181,6 +181,19 @@ TEST(pthread, pthread_key_dirty) { ASSERT_EQ(0, pthread_key_delete(key)); } +TEST(pthread, static_pthread_key_used_before_creation) { +#if defined(__BIONIC__) + // See http://b/19625804. The bug is about a static/global pthread key being used before creation. + // So here tests if the static/global default value 0 can be detected as invalid key. + static pthread_key_t key; + ASSERT_EQ(nullptr, pthread_getspecific(key)); + ASSERT_EQ(EINVAL, pthread_setspecific(key, nullptr)); + ASSERT_EQ(EINVAL, pthread_key_delete(key)); +#else + GTEST_LOG_(INFO) << "This test tests bionic pthread key implementation detail.\n"; +#endif +} + static void* IdFn(void* arg) { return arg; } From 605ee81b0660760dd191f2e2de4c4c92dca5192f Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 13 Apr 2015 14:20:11 -0700 Subject: [PATCH 078/123] Add missing cfi directives for x86 assembler. Change-Id: I80d3e33a71bbaeab5f39b667ebe61e865fd54b80 --- libc/arch-x86/bionic/__bionic_clone.S | 12 ++++++++++++ libc/arch-x86/bionic/syscall.S | 16 ++++++++++++++++ libc/arch-x86/bionic/vfork.S | 2 ++ 3 files changed, 30 insertions(+) diff --git a/libc/arch-x86/bionic/__bionic_clone.S b/libc/arch-x86/bionic/__bionic_clone.S index ef78aeef7..1a6f64259 100644 --- a/libc/arch-x86/bionic/__bionic_clone.S +++ b/libc/arch-x86/bionic/__bionic_clone.S @@ -3,8 +3,14 @@ // pid_t __bionic_clone(int flags, void* child_stack, pid_t* parent_tid, void* tls, pid_t* child_tid, int (*fn)(void*), void* arg); ENTRY(__bionic_clone) pushl %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebx, 0 pushl %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 pushl %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 # Load system call arguments into registers. movl 16(%esp), %ebx # flags @@ -46,8 +52,14 @@ ENTRY(__bionic_clone) # We're the parent; nothing to do. .L_bc_return: popl %edi + .cfi_adjust_cfa_offset -4 + .cfi_restore edi popl %esi + .cfi_adjust_cfa_offset -4 + .cfi_restore esi popl %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore ebx ret END(__bionic_clone) .hidden __bionic_clone diff --git a/libc/arch-x86/bionic/syscall.S b/libc/arch-x86/bionic/syscall.S index f85ec3904..963e4c566 100644 --- a/libc/arch-x86/bionic/syscall.S +++ b/libc/arch-x86/bionic/syscall.S @@ -15,9 +15,17 @@ ENTRY(syscall) # Push the callee save registers. push %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebx, 0 push %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 push %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 push %ebp + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebp, 0 # Load all the arguments from the calling frame. # (Not all will be valid, depending on the syscall.) @@ -43,8 +51,16 @@ ENTRY(syscall) 1: # Restore the callee save registers. pop %ebp + .cfi_adjust_cfa_offset -4 + .cfi_restore ebp, 0 pop %edi + .cfi_adjust_cfa_offset -4 + .cfi_restore edi, 0 pop %esi + .cfi_adjust_cfa_offset -4 + .cfi_restore esi, 0 pop %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore ebx, 0 ret END(syscall) diff --git a/libc/arch-x86/bionic/vfork.S b/libc/arch-x86/bionic/vfork.S index 6c0291088..ca7af0f7a 100644 --- a/libc/arch-x86/bionic/vfork.S +++ b/libc/arch-x86/bionic/vfork.S @@ -32,6 +32,8 @@ ENTRY(vfork) popl %ecx // Grab the return address. + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ecx, 0 movl $__NR_vfork, %eax int $0x80 cmpl $-MAX_ERRNO, %eax From 940d3122c9c88eb3e46f8862a02a668fa0f4cf2b Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 14 Apr 2015 17:02:31 -0700 Subject: [PATCH 079/123] Fix addition of extra arg to cfi_restore. Change-Id: I8fdcc1ae3e91b69ccbcec756a89e1ccb4fa1be53 --- libc/arch-x86/bionic/syscall.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libc/arch-x86/bionic/syscall.S b/libc/arch-x86/bionic/syscall.S index 963e4c566..2a1510273 100644 --- a/libc/arch-x86/bionic/syscall.S +++ b/libc/arch-x86/bionic/syscall.S @@ -52,15 +52,15 @@ ENTRY(syscall) # Restore the callee save registers. pop %ebp .cfi_adjust_cfa_offset -4 - .cfi_restore ebp, 0 + .cfi_restore ebp pop %edi .cfi_adjust_cfa_offset -4 - .cfi_restore edi, 0 + .cfi_restore edi pop %esi .cfi_adjust_cfa_offset -4 - .cfi_restore esi, 0 + .cfi_restore esi pop %ebx .cfi_adjust_cfa_offset -4 - .cfi_restore ebx, 0 + .cfi_restore ebx ret END(syscall) From 66aa0b61f736678e97a0cfaf975052881a23651b Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 15 Apr 2015 14:07:28 -0700 Subject: [PATCH 080/123] Hide emutls* symbols in libc.so Also make thread_local in test static to avoid ld.bfd warning for arm64. Change-Id: I09a3f2aa9b73a4fafa3f3bbc64ddc2a128ad50ee --- libc/Android.mk | 2 +- libc/bionic/__cxa_thread_atexit_impl.cpp | 2 +- tests/__cxa_thread_atexit_test.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libc/Android.mk b/libc/Android.mk index e632ee72f..78b847584 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1007,7 +1007,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files) -LOCAL_CFLAGS := $(libc_common_cflags) -fno-data-sections -Wframe-larger-than=2048 +LOCAL_CFLAGS := $(libc_common_cflags) -Wframe-larger-than=2048 LOCAL_CONLYFLAGS := $(libc_common_conlyflags) LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp index 9ae6dfda1..0e427d346 100644 --- a/libc/bionic/__cxa_thread_atexit_impl.cpp +++ b/libc/bionic/__cxa_thread_atexit_impl.cpp @@ -22,7 +22,7 @@ struct thread_local_dtor { thread_local_dtor* next; }; -__thread thread_local_dtor* thread_local_dtors = nullptr; +static __thread thread_local_dtor* thread_local_dtors = nullptr; extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) { thread_local_dtor* dtor = new thread_local_dtor(); diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp index fea60b719..83aab53cc 100644 --- a/tests/__cxa_thread_atexit_test.cpp +++ b/tests/__cxa_thread_atexit_test.cpp @@ -35,7 +35,7 @@ class ClassWithDtor { std::string message; }; -thread_local ClassWithDtor class_with_dtor; +static thread_local ClassWithDtor class_with_dtor; static void* thread_nop(void* arg) { class_with_dtor.set_message(*static_cast(arg)); From 163ab8ba86deb991c73152e6828f270cc71dc4c5 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 15 Apr 2015 15:31:51 -0700 Subject: [PATCH 081/123] Call __cxa_thread_finalize for the main thread. Bug: http://b/20231984 Bug: http://b/16696563 Change-Id: I71cfddd0d404d1d4a593ec8d3bca9741de8cb90f --- libc/Android.mk | 2 +- .../lib/libc => }/stdlib/exit.c | 11 +++++++-- tests/__cxa_thread_atexit_test.cpp | 23 +++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) rename libc/{upstream-openbsd/lib/libc => }/stdlib/exit.c (88%) diff --git a/libc/Android.mk b/libc/Android.mk index e632ee72f..0dc3db770 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -63,6 +63,7 @@ libc_common_src_files := \ stdio/sprintf.c \ stdio/stdio.c \ stdio/stdio_ext.cpp \ + stdlib/exit.c \ # Fortify implementations of libc functions. libc_common_src_files += \ @@ -480,7 +481,6 @@ libc_upstream_openbsd_ndk_src_files := \ upstream-openbsd/lib/libc/stdlib/atoi.c \ upstream-openbsd/lib/libc/stdlib/atol.c \ upstream-openbsd/lib/libc/stdlib/atoll.c \ - upstream-openbsd/lib/libc/stdlib/exit.c \ upstream-openbsd/lib/libc/stdlib/getenv.c \ upstream-openbsd/lib/libc/stdlib/insque.c \ upstream-openbsd/lib/libc/stdlib/lsearch.c \ diff --git a/libc/upstream-openbsd/lib/libc/stdlib/exit.c b/libc/stdlib/exit.c similarity index 88% rename from libc/upstream-openbsd/lib/libc/stdlib/exit.c rename to libc/stdlib/exit.c index 83fe3d2de..10ce674ab 100644 --- a/libc/upstream-openbsd/lib/libc/stdlib/exit.c +++ b/libc/stdlib/exit.c @@ -32,8 +32,6 @@ #include #include #include -#include "atexit.h" -#include "thread_private.h" /* * This variable is zero until a process has created a thread. @@ -44,12 +42,21 @@ */ int __isthreaded = 0; +/* BEGIN android-added: using __cxa_finalize and __cxa_thread_finalize */ +extern void __cxa_finalize(void* dso_handle); +extern void __cxa_thread_finalize(); +/* END android-added */ + /* * Exit, flushing stdio buffers if necessary. */ void exit(int status) { + /* BEGIN android-added: call thread_local d-tors */ + __cxa_thread_finalize(); + /* END android-added */ + /* * Call functions registered by atexit() or _cxa_atexit() * (including the stdio cleanup routine) and then _exit(). diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp index fea60b719..59d2efd50 100644 --- a/tests/__cxa_thread_atexit_test.cpp +++ b/tests/__cxa_thread_atexit_test.cpp @@ -50,6 +50,29 @@ TEST(thread_local, smoke) { ASSERT_EQ("dtor called.", class_with_dtor_output); } +class ClassWithDtorForMainThread { + public: + void set_message(const std::string& msg) { + message = msg; + } + + ~ClassWithDtorForMainThread() { + fprintf(stderr, "%s", message.c_str()); + } + private: + std::string message; +}; + +static void thread_atexit_main() { + static thread_local ClassWithDtorForMainThread class_with_dtor_for_main_thread; + class_with_dtor_for_main_thread.set_message("d-tor for main thread called."); + exit(0); +} + +TEST(thread_local, dtor_for_main_thread) { + ASSERT_EXIT(thread_atexit_main(), testing::ExitedWithCode(0), "d-tor for main thread called."); +} + extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle); static void thread_atexit_fn1(void* arg) { From 43e020ce936aa18e071664de198adfaa71af2d82 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 15 Apr 2015 17:03:43 -0700 Subject: [PATCH 082/123] Remove PROP_PATH_SYSTEM_DEFAULT. Change-Id: Ib01d9c2f9d890eb5e7ba1e15bd11767195e84967 --- libc/include/sys/_system_properties.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h index 44fe9919f..7ff3ded07 100644 --- a/libc/include/sys/_system_properties.h +++ b/libc/include/sys/_system_properties.h @@ -80,7 +80,6 @@ struct prop_msg #define PROP_PATH_RAMDISK_DEFAULT "/default.prop" #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" -#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" #define PROP_PATH_VENDOR_BUILD "/vendor/build.prop" #define PROP_PATH_BOOTIMAGE_BUILD "/build.prop" #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" From c0f89283cc746ff7d6f6be405c603435b8b02657 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 15 Apr 2015 16:34:57 -0700 Subject: [PATCH 083/123] Update the number of jemalloc reserved keys. jemalloc now uses a single key pointing to a structure rather than multiple keys. Change-Id: Ib76185a594ab2cd4dc400d9a7a5bc0a57a7ac92d --- libc/private/bionic_tls.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h index 414d1711d..30dc0eb3e 100644 --- a/libc/private/bionic_tls.h +++ b/libc/private/bionic_tls.h @@ -97,14 +97,8 @@ enum { #define LIBC_PTHREAD_KEY_RESERVED_COUNT 12 #if defined(USE_JEMALLOC) -/* Following are current pthread keys used internally by jemalloc: - * je_thread_allocated_tsd jemalloc - * je_arenas_tsd jemalloc - * je_tcache_tsd jemalloc - * je_tcache_enabled_tsd jemalloc - * je_quarantine_tsd jemalloc - */ -#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 5 +/* Internally, jemalloc uses a single key for per thread data. */ +#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 1 #define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT) #else #define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT From 95f1ee235ae257802a94d7e94d476ea0aaea5cd8 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 13 Jan 2015 19:53:15 -0800 Subject: [PATCH 084/123] Change on handling of SIGEV_THREAD timers. 1. Don't prevent calling callback when SIGEV_THREAD timers are disarmed by timer_settime. As in POSIX standard: The effect of disarming or resetting a timer with pending expiration notifications is unspecified. And glibc didn't prevent in this situation, so I think it is fine to remove the support. 2. Still prevent calling callback when SIGEV_THREAD timers are deleted by timer_delete. As in POSIX standard: The disposition of pending signals for the deleted timer is unspecified. However, glibc handles this (although that is not perfect). And some of our tests in time_test.cpp depend on this feature as described in b/18039727. so I retain the support. 3. Fix some flaky test in time_test.cpp, and make "time*" test pass on bionic-unit-tests-glibcxx. Bug: 18263854 Change-Id: I8ced184eacdbfcf433fd81b0c69c38824beb8ebc --- libc/bionic/posix_timers.cpp | 42 +++++------ tests/time_test.cpp | 142 ++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp index 999157371..bc3aeb21c 100644 --- a/libc/bionic/posix_timers.cpp +++ b/libc/bionic/posix_timers.cpp @@ -26,14 +26,15 @@ * SUCH DAMAGE. */ -#include "pthread_internal.h" -#include "private/bionic_futex.h" #include "private/kernel_sigset_t.h" #include #include +#include +#include #include #include +#include // System calls. extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t); @@ -59,11 +60,11 @@ struct PosixTimer { int sigev_notify; - // These fields are only needed for a SIGEV_THREAD timer. + // The fields below are only needed for a SIGEV_THREAD timer. pthread_t callback_thread; void (*callback)(sigval_t); sigval_t callback_argument; - volatile bool armed; + atomic_bool deleted; // Set when the timer is deleted, to prevent further calling of callback. }; static __kernel_timer_t to_kernel_timer_id(timer_t timer) { @@ -85,8 +86,13 @@ static void* __timer_thread_start(void* arg) { continue; } - if (si.si_code == SI_TIMER && timer->armed) { + if (si.si_code == SI_TIMER) { // This signal was sent because a timer fired, so call the callback. + + // All events to the callback will be ignored when the timer is deleted. + if (atomic_load(&timer->deleted) == true) { + continue; + } timer->callback(timer->callback_argument); } else if (si.si_code == SI_TKILL) { // This signal was sent because someone wants us to exit. @@ -97,9 +103,7 @@ static void* __timer_thread_start(void* arg) { } static void __timer_thread_stop(PosixTimer* timer) { - // Immediately mark the timer as disarmed so even if some events - // continue to happen, the callback won't be called. - timer->armed = false; + atomic_store(&timer->deleted, true); pthread_kill(timer->callback_thread, TIMER_SIGNAL); } @@ -126,7 +130,7 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) { // Otherwise, this must be SIGEV_THREAD timer... timer->callback = evp->sigev_notify_function; timer->callback_argument = evp->sigev_value; - timer->armed = false; + atomic_init(&timer->deleted, false); // Check arguments that the kernel doesn't care about but we do. if (timer->callback == NULL) { @@ -199,25 +203,19 @@ int timer_delete(timer_t id) { return 0; } -// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html +// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_gettime.html int timer_gettime(timer_t id, itimerspec* ts) { return __timer_gettime(to_kernel_timer_id(id), ts); } -// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html +// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_settime.html +// When using timer_settime to disarm a repeatable SIGEV_THREAD timer with a very small +// period (like below 1ms), the kernel may continue to send events to the callback thread +// for a few extra times. This behavior is fine because in POSIX standard: The effect of +// disarming or resetting a timer with pending expiration notifications is unspecified. int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) { PosixTimer* timer= reinterpret_cast(id); - int rc = __timer_settime(timer->kernel_timer_id, flags, ts, ots); - if (rc == 0) { - // Mark the timer as either being armed or disarmed. This avoids the - // callback being called after the disarm for SIGEV_THREAD timers only. - if (ts->it_value.tv_sec != 0 || ts->it_value.tv_nsec != 0) { - timer->armed = true; - } else { - timer->armed = false; - } - } - return rc; + return __timer_settime(timer->kernel_timer_id, flags, ts, ots); } // http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html diff --git a/tests/time_test.cpp b/tests/time_test.cpp index 691d8ff9c..a0b0209ab 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "ScopedSignalHandler.h" @@ -197,7 +198,7 @@ TEST(time, timer_create) { ASSERT_EQ(0, timer_delete(timer_id)); } -static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0; +static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count; static void timer_create_SIGEV_SIGNAL_signal_handler(int signal_number) { ++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count; ASSERT_EQ(SIGUSR1, signal_number); @@ -212,6 +213,7 @@ TEST(time, timer_create_SIGEV_SIGNAL) { timer_t timer_id; ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id)); + timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0; ScopedSignalHandler ssh(SIGUSR1, timer_create_SIGEV_SIGNAL_signal_handler); ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count); @@ -228,25 +230,26 @@ TEST(time, timer_create_SIGEV_SIGNAL) { } struct Counter { - volatile int value; + private: + std::atomic value; timer_t timer_id; sigevent_t se; bool timer_valid; - Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) { - memset(&se, 0, sizeof(se)); - se.sigev_notify = SIGEV_THREAD; - se.sigev_notify_function = fn; - se.sigev_value.sival_ptr = this; - Create(); - } - void Create() { ASSERT_FALSE(timer_valid); ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id)); timer_valid = true; } + public: + Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) { + memset(&se, 0, sizeof(se)); + se.sigev_notify = SIGEV_THREAD; + se.sigev_notify_function = fn; + se.sigev_value.sival_ptr = this; + Create(); + } void DeleteTimer() { ASSERT_TRUE(timer_valid); ASSERT_EQ(0, timer_delete(timer_id)); @@ -259,12 +262,16 @@ struct Counter { } } + int Value() const { + return value; + } + void SetTime(time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) { ::SetTime(timer_id, value_s, value_ns, interval_s, interval_ns); } bool ValueUpdated() { - volatile int current_value = value; + int current_value = value; time_t start = time(NULL); while (current_value == value && (time(NULL) - start) < 5) { } @@ -287,30 +294,29 @@ struct Counter { TEST(time, timer_settime_0) { Counter counter(Counter::CountAndDisarmNotifyFunction); - ASSERT_TRUE(counter.timer_valid); - - ASSERT_EQ(0, counter.value); + ASSERT_EQ(0, counter.Value()); counter.SetTime(0, 1, 1, 0); usleep(500000); // The count should just be 1 because we disarmed the timer the first time it fired. - ASSERT_EQ(1, counter.value); + ASSERT_EQ(1, counter.Value()); } TEST(time, timer_settime_repeats) { Counter counter(Counter::CountNotifyFunction); - ASSERT_TRUE(counter.timer_valid); - - ASSERT_EQ(0, counter.value); + ASSERT_EQ(0, counter.Value()); counter.SetTime(0, 1, 0, 10); ASSERT_TRUE(counter.ValueUpdated()); ASSERT_TRUE(counter.ValueUpdated()); ASSERT_TRUE(counter.ValueUpdated()); + counter.DeleteTimer(); + // Add a sleep as other threads may be calling the callback function when the timer is deleted. + usleep(500000); } -static int timer_create_NULL_signal_handler_invocation_count = 0; +static int timer_create_NULL_signal_handler_invocation_count; static void timer_create_NULL_signal_handler(int signal_number) { ++timer_create_NULL_signal_handler_invocation_count; ASSERT_EQ(SIGALRM, signal_number); @@ -321,6 +327,7 @@ TEST(time, timer_create_NULL) { timer_t timer_id; ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &timer_id)); + timer_create_NULL_signal_handler_invocation_count = 0; ScopedSignalHandler ssh(SIGALRM, timer_create_NULL_signal_handler); ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count); @@ -367,22 +374,59 @@ TEST(time, timer_delete_multiple) { TEST(time, timer_create_multiple) { Counter counter1(Counter::CountNotifyFunction); - ASSERT_TRUE(counter1.timer_valid); Counter counter2(Counter::CountNotifyFunction); - ASSERT_TRUE(counter2.timer_valid); Counter counter3(Counter::CountNotifyFunction); - ASSERT_TRUE(counter3.timer_valid); - ASSERT_EQ(0, counter1.value); - ASSERT_EQ(0, counter2.value); - ASSERT_EQ(0, counter3.value); + ASSERT_EQ(0, counter1.Value()); + ASSERT_EQ(0, counter2.Value()); + ASSERT_EQ(0, counter3.Value()); counter2.SetTime(0, 1, 0, 0); usleep(500000); - EXPECT_EQ(0, counter1.value); - EXPECT_EQ(1, counter2.value); - EXPECT_EQ(0, counter3.value); + EXPECT_EQ(0, counter1.Value()); + EXPECT_EQ(1, counter2.Value()); + EXPECT_EQ(0, counter3.Value()); +} + +// Test to verify that disarming a repeatable timer disables the callbacks. +TEST(time, timer_disarm_terminates) { + Counter counter(Counter::CountNotifyFunction); + ASSERT_EQ(0, counter.Value()); + + counter.SetTime(0, 1, 0, 1); + ASSERT_TRUE(counter.ValueUpdated()); + ASSERT_TRUE(counter.ValueUpdated()); + ASSERT_TRUE(counter.ValueUpdated()); + + counter.SetTime(0, 0, 0, 0); + // Add a sleep as the kernel may have pending events when the timer is disarmed. + usleep(500000); + int value = counter.Value(); + usleep(500000); + + // Verify the counter has not been incremented. + ASSERT_EQ(value, counter.Value()); +} + +// Test to verify that deleting a repeatable timer disables the callbacks. +TEST(time, timer_delete_terminates) { + Counter counter(Counter::CountNotifyFunction); + ASSERT_EQ(0, counter.Value()); + + counter.SetTime(0, 1, 0, 1); + ASSERT_TRUE(counter.ValueUpdated()); + ASSERT_TRUE(counter.ValueUpdated()); + ASSERT_TRUE(counter.ValueUpdated()); + + counter.DeleteTimer(); + // Add a sleep as other threads may be calling the callback function when the timer is deleted. + usleep(500000); + int value = counter.Value(); + usleep(500000); + + // Verify the counter has not been incremented. + ASSERT_EQ(value, counter.Value()); } struct TimerDeleteData { @@ -499,45 +543,3 @@ TEST(time, clock_nanosleep) { timespec out; ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &out)); } - -// Test to verify that disarming a repeatable timer disables the -// callbacks. -TEST(time, timer_disarm_terminates) { - Counter counter(Counter::CountNotifyFunction); - ASSERT_TRUE(counter.timer_valid); - - ASSERT_EQ(0, counter.value); - - counter.SetTime(0, 1, 0, 1); - ASSERT_TRUE(counter.ValueUpdated()); - ASSERT_TRUE(counter.ValueUpdated()); - ASSERT_TRUE(counter.ValueUpdated()); - - counter.SetTime(0, 0, 1, 0); - volatile int value = counter.value; - usleep(500000); - - // Verify the counter has not been incremented. - ASSERT_EQ(value, counter.value); -} - -// Test to verify that deleting a repeatable timer disables the -// callbacks. -TEST(time, timer_delete_terminates) { - Counter counter(Counter::CountNotifyFunction); - ASSERT_TRUE(counter.timer_valid); - - ASSERT_EQ(0, counter.value); - - counter.SetTime(0, 1, 0, 1); - ASSERT_TRUE(counter.ValueUpdated()); - ASSERT_TRUE(counter.ValueUpdated()); - ASSERT_TRUE(counter.ValueUpdated()); - - counter.DeleteTimer(); - volatile int value = counter.value; - usleep(500000); - - // Verify the counter has not been incremented. - ASSERT_EQ(value, counter.value); -} From e7e1c875b0f8eefb1d771f200a58f54e64c39d55 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Thu, 16 Apr 2015 09:07:45 -0400 Subject: [PATCH 085/123] add fortified implementations of pread/pread64 Change-Id: Iec39c3917e0bc94371bd81541619392f5abe29b9 --- libc/Android.mk | 2 ++ libc/bionic/__pread64_chk.cpp | 43 ++++++++++++++++++++++ libc/bionic/__pread_chk.cpp | 43 ++++++++++++++++++++++ libc/include/unistd.h | 67 +++++++++++++++++++++++++++++++++++ tests/fortify_test.cpp | 16 +++++++++ 5 files changed, 171 insertions(+) create mode 100644 libc/bionic/__pread64_chk.cpp create mode 100644 libc/bionic/__pread_chk.cpp diff --git a/libc/Android.mk b/libc/Android.mk index e06a86056..9d3b62aeb 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -71,6 +71,8 @@ libc_common_src_files += \ bionic/__fgets_chk.cpp \ bionic/__memmove_chk.cpp \ bionic/__poll_chk.cpp \ + bionic/__pread64_chk.cpp \ + bionic/__pread_chk.cpp \ bionic/__read_chk.cpp \ bionic/__recvfrom_chk.cpp \ bionic/__stpcpy_chk.cpp \ diff --git a/libc/bionic/__pread64_chk.cpp b/libc/bionic/__pread64_chk.cpp new file mode 100644 index 000000000..5d6ad2d98 --- /dev/null +++ b/libc/bionic/__pread64_chk.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" ssize_t __pread64_chk(int fd, void* buf, size_t count, off64_t offset, size_t buf_size) { + if (__predict_false(count > buf_size)) { + __fortify_chk_fail("pread64: prevented write past end of buffer", 0); + } + + if (__predict_false(count > SSIZE_MAX)) { + __fortify_chk_fail("pread64: count > SSIZE_MAX", 0); + } + + return pread64(fd, buf, count, offset); +} diff --git a/libc/bionic/__pread_chk.cpp b/libc/bionic/__pread_chk.cpp new file mode 100644 index 000000000..7109ce681 --- /dev/null +++ b/libc/bionic/__pread_chk.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" ssize_t __pread_chk(int fd, void* buf, size_t count, off_t offset, size_t buf_size) { + if (__predict_false(count > buf_size)) { + __fortify_chk_fail("pread: prevented write past end of buffer", 0); + } + + if (__predict_false(count > SSIZE_MAX)) { + __fortify_chk_fail("pread: count > SSIZE_MAX", 0); + } + + return pread(fd, buf, count, offset); +} diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 92d3abe3c..a601cb79a 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -224,6 +224,16 @@ extern int tcsetpgrp(int fd, pid_t _pid); } while (_rc == -1 && errno == EINTR); \ _rc; }) +extern ssize_t __pread_chk(int, void*, size_t, off_t, size_t); +__errordecl(__pread_dest_size_error, "pread called with size bigger than destination"); +__errordecl(__pread_count_toobig_error, "pread called with count > SSIZE_MAX"); +extern ssize_t __pread_real(int, void*, size_t, off_t) __RENAME(pread); + +extern ssize_t __pread64_chk(int, void*, size_t, off64_t, size_t); +__errordecl(__pread64_dest_size_error, "pread64 called with size bigger than destination"); +__errordecl(__pread64_count_toobig_error, "pread64 called with count > SSIZE_MAX"); +extern ssize_t __pread64_real(int, void*, size_t, off64_t) __RENAME(pread64); + extern ssize_t __read_chk(int, void*, size_t, size_t); __errordecl(__read_dest_size_error, "read called with size bigger than destination"); __errordecl(__read_count_toobig_error, "read called with count > SSIZE_MAX"); @@ -231,6 +241,62 @@ extern ssize_t __read_real(int, void*, size_t) __RENAME(read); #if defined(__BIONIC_FORTIFY) +#if defined(__USE_FILE_OFFSET64) +#define __PREAD_PREFIX(x) __pread64_ ## x +#else +#define __PREAD_PREFIX(x) __pread_ ## x +#endif + +__BIONIC_FORTIFY_INLINE +ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + size_t bos = __bos0(buf); + +#if !defined(__clang__) + if (__builtin_constant_p(count) && (count > SSIZE_MAX)) { + __PREAD_PREFIX(count_toobig_error)(); + } + + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __PREAD_PREFIX(real)(fd, buf, count, offset); + } + + if (__builtin_constant_p(count) && (count > bos)) { + __PREAD_PREFIX(dest_size_error)(); + } + + if (__builtin_constant_p(count) && (count <= bos)) { + return __PREAD_PREFIX(real)(fd, buf, count, offset); + } +#endif + + return __PREAD_PREFIX(chk)(fd, buf, count, offset, bos); +} + +__BIONIC_FORTIFY_INLINE +ssize_t pread64(int fd, void* buf, size_t count, off64_t offset) { + size_t bos = __bos0(buf); + +#if !defined(__clang__) + if (__builtin_constant_p(count) && (count > SSIZE_MAX)) { + __pread64_count_toobig_error(); + } + + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __pread64_real(fd, buf, count, offset); + } + + if (__builtin_constant_p(count) && (count > bos)) { + __pread64_dest_size_error(); + } + + if (__builtin_constant_p(count) && (count <= bos)) { + return __pread64_real(fd, buf, count, offset); + } +#endif + + return __pread64_chk(fd, buf, count, offset, bos); +} + __BIONIC_FORTIFY_INLINE ssize_t read(int fd, void* buf, size_t count) { size_t bos = __bos0(buf); @@ -255,6 +321,7 @@ ssize_t read(int fd, void* buf, size_t count) { return __read_chk(fd, buf, count, bos); } + #endif /* defined(__BIONIC_FORTIFY) */ __END_DECLS diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp index 5cc728f29..70159de3c 100644 --- a/tests/fortify_test.cpp +++ b/tests/fortify_test.cpp @@ -623,6 +623,22 @@ TEST_F(DEATHTEST, FD_ISSET_2_fortified) { ASSERT_FORTIFY(FD_ISSET(0, set)); } +TEST_F(DEATHTEST, pread_fortified) { + char buf[1]; + size_t ct = atoi("2"); // prevent optimizations + int fd = open("/dev/null", O_RDONLY); + ASSERT_FORTIFY(pread(fd, buf, ct, 0)); + close(fd); +} + +TEST_F(DEATHTEST, pread64_fortified) { + char buf[1]; + size_t ct = atoi("2"); // prevent optimizations + int fd = open("/dev/null", O_RDONLY); + ASSERT_FORTIFY(pread64(fd, buf, ct, 0)); + close(fd); +} + TEST_F(DEATHTEST, read_fortified) { char buf[1]; size_t ct = atoi("2"); // prevent optimizations From a4061cddbefc3fd1c7eeb2ca270fd653a43372f1 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 16 Apr 2015 14:20:13 -0700 Subject: [PATCH 086/123] Clean up "logging". Print is bad and I should feel bad. Use the logging module instead. Will follow up by adding a persistent log destination instead of just the console. Change-Id: I396ff10712f88a03f8d8183b6de29ea273815962 --- tools/bionicbb/README.md | 1 - tools/bionicbb/build_listener.py | 24 ++++----- tools/bionicbb/gmail_listener.py | 89 +++++++++----------------------- 3 files changed, 37 insertions(+), 77 deletions(-) diff --git a/tools/bionicbb/README.md b/tools/bionicbb/README.md index 91f64d854..4d3291fc3 100644 --- a/tools/bionicbb/README.md +++ b/tools/bionicbb/README.md @@ -12,7 +12,6 @@ Dependencies * [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation) * [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi) * [Requests](http://docs.python-requests.org/en/latest/) - * [termcolor](https://pypi.python.org/pypi/termcolor) Setup ----- diff --git a/tools/bionicbb/build_listener.py b/tools/bionicbb/build_listener.py index f7f52eddc..fa55d37d6 100644 --- a/tools/bionicbb/build_listener.py +++ b/tools/bionicbb/build_listener.py @@ -15,8 +15,8 @@ # limitations under the License. # import json +import logging import requests -import termcolor import gerrit @@ -43,7 +43,7 @@ def handle_build_message(): ref = params['REF'] patch_set = ref.split('/')[-1] - print '{} #{} {}: {}'.format(name, number, status, full_url) + logging.debug('%s #%s %s: %s', name, number, status, full_url) # bionic-lint is always broken, so we don't want to reject changes for # those failures until we clean things up. @@ -69,19 +69,19 @@ def handle_build_message(): patch_set)) headers = {'Content-Type': 'application/json;charset=UTF-8'} - print 'POST {}: {}'.format(url, request_data) - print requests.post(url, headers=headers, json=request_data) + logging.debug('POST %s: %s', url, request_data) + requests.post(url, headers=headers, json=request_data) elif name == 'clean-bionic-presubmit': request_data = {'message': 'out/ directory removed'} url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id, patch_set)) headers = {'Content-Type': 'application/json;charset=UTF-8'} - print 'POST {}: {}'.format(url, request_data) - print requests.post(url, headers=headers, json=request_data) + logging.debug('POST %s: %s', url, request_data) + requests.post(url, headers=headers, json=request_data) elif name == 'bionic-lint': - print 'IGNORED' + logging.warning('Result for bionic-lint ignored') else: - print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name) + logging.error('Unknown project: %s', name) return '' @@ -100,17 +100,17 @@ def drop_rejection(): bb_review = 0 if bb_review >= 0: - print 'No rejection to drop: {} {}'.format(change_id, patch_set) + logging.info('No rejection to drop: %s %s', change_id, patch_set) return '' - print 'Dropping rejection: {} {}'.format(change_id, patch_set) + logging.info('Dropping rejection: %s %s', change_id, patch_set) request_data = {'labels': {'Verified': 0}} url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id, patch_set)) headers = {'Content-Type': 'application/json;charset=UTF-8'} - print 'POST {}: {}'.format(url, request_data) - print requests.post(url, headers=headers, json=request_data) + logging.debug('POST %s: %s', url, request_data) + requests.post(url, headers=headers, json=request_data) return '' diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py index dd0c008b1..134258a78 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/gmail_listener.py @@ -19,10 +19,10 @@ import httplib import httplib2 import jenkinsapi import json +import logging import os import re import requests -import termcolor import socket import sys import time @@ -141,7 +141,7 @@ def get_gerrit_info(body): return info -def clean_project(gerrit_info, dry_run): +def clean_project(dry_run): username = config.jenkins_credentials['username'] password = config.jenkins_credentials['password'] jenkins_url = config.jenkins_url @@ -154,16 +154,9 @@ def clean_project(gerrit_info, dry_run): url = job.get_build().baseurl else: url = 'DRY_RUN_URL' - print '{}({}): {} {}'.format( - termcolor.colored('CLEAN', 'green'), - gerrit_info['MessageType'], - build, - url) + logging.info('Cleaning: %s %s', build, url) else: - print '{}({}): {}'.format( - termcolor.colored('CLEAN', 'red'), - gerrit_info['MessageType'], - termcolor.colored(build, 'red')) + logging.error('Failed to clean: could not find project %s', build) return True @@ -194,21 +187,9 @@ def build_project(gerrit_info, dry_run, lunch_target=None): if not project_path: raise RuntimeError('bogus project: {}'.format(project)) if project_path.startswith('platform/'): - print '{}({}): {} => {}'.format( - termcolor.colored('ERROR', 'red'), - 'project', - project, - project_path) - return False - try: - ref = gerrit.ref_for_change(change_id) - except gerrit.GerritError as ex: - print '{}({}): {} {}'.format( - termcolor.colored('GERRIT-ERROR', 'red'), - ex.code, - change_id, - ex.url) - return False + raise RuntimeError('Bad project mapping: {} => {}'.format( + project, project_path)) + ref = gerrit.ref_for_change(change_id) params = { 'REF': ref, 'CHANGE_ID': change_id, @@ -223,20 +204,10 @@ def build_project(gerrit_info, dry_run, lunch_target=None): url = 'URL UNAVAILABLE' else: url = 'DRY_RUN_URL' - print '{}({}): {} => {} {} {}'.format( - termcolor.colored('BUILD', 'green'), - gerrit_info['MessageType'], - project, - build, - url, - change_id) + logging.info('Building: %s => %s %s %s', project, build, url, + change_id) else: - print '{}({}): {} => {} {}'.format( - termcolor.colored('BUILD', 'red'), - gerrit_info['MessageType'], - project, - termcolor.colored(build, 'red'), - change_id) + logging.error('Unknown build: %s => %s %s', project, build, change_id) return True @@ -259,13 +230,9 @@ def drop_rejection(gerrit_info, dry_run): try: requests.post(url, headers=headers, data=json.dumps(request_data)) except requests.exceptions.ConnectionError as ex: - print '{}(drop-rejection): {}'.format( - termcolor.colored('ERROR', 'red'), ex) + logging.error('Failed to drop rejection: %s', ex) return False - print '{}({}): {}'.format( - termcolor.colored('CHECK', 'green'), - gerrit_info['MessageType'], - gerrit_info['Change-Id']) + logging.info('Dropped rejection: %s', gerrit_info['Change-Id']) return True @@ -277,7 +244,7 @@ def handle_comment(gerrit_info, body, dry_run): return True command_map = { - 'clean': lambda: clean_project(gerrit_info, dry_run), + 'clean': lambda: clean_project(dry_run), 'retry': lambda: build_project(gerrit_info, dry_run), 'arm': lambda: build_project(gerrit_info, dry_run, @@ -310,11 +277,11 @@ def handle_comment(gerrit_info, body, dry_run): def skip_handler(gerrit_info, _, __): - print '{}({}): {}'.format( - termcolor.colored('SKIP', 'yellow'), - gerrit_info['MessageType'], - gerrit_info['Change-Id']) + logging.info('Skipping %s: %s', gerrit_info['MessageType'], + gerrit_info['Change-Id']) return True + + handle_abandon = skip_handler handle_merge_failed = skip_handler handle_merged = skip_handler @@ -327,28 +294,22 @@ def process_message(msg, dry_run): body = get_body(msg) gerrit_info = get_gerrit_info(body) if not gerrit_info: - print termcolor.colored('No info found: {}'.format(msg['id']), - 'red') + logging.fatal('No Gerrit info found: %s', msg.subject) msg_type = gerrit_info['MessageType'] handler = 'handle_{}'.format( gerrit_info['MessageType'].replace('-', '_')) if handler in globals(): return globals()[handler](gerrit_info, body, dry_run) else: - print termcolor.colored( - 'MessageType {} unhandled.'.format(msg_type), 'red') - print + logging.warning('MessageType %s unhandled.', msg_type) return False except NotImplementedError as ex: - print ex + logging.error("%s", ex) return False except gerrit.GerritError as ex: - if ex.code == 404: - print '{}(404): {}!'.format( - termcolor.colored('ERROR', 'red'), ex) - return True - else: - return False + change_id = gerrit_info['Change-Id'] + logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url) + return ex.code == 404 def main(argc, argv): @@ -376,10 +337,10 @@ def main(argc, argv): msg_service.trash(userId='me', id=msg['id']).execute() time.sleep(60 * 5) except GmailError as ex: - print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex) + logging.error('Gmail error: %s', ex) time.sleep(60 * 5) except apiclient.errors.HttpError as ex: - print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex) + logging.error('API Client HTTP error: %s', ex) time.sleep(60 * 5) except httplib.BadStatusLine: pass From a9325133aad79c079bf34aafa85c0544ef940f8c Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 16 Apr 2015 17:56:12 -0700 Subject: [PATCH 087/123] Fix clang build. Change-Id: I70a9ebe806cb4f7e23a7d8e486157ddd70ae3008 --- libc/bionic/legacy_32_bit_support.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp index 50e4643ce..a10766471 100644 --- a/libc/bionic/legacy_32_bit_support.cpp +++ b/libc/bionic/legacy_32_bit_support.cpp @@ -26,6 +26,8 @@ * SUCH DAMAGE. */ +#undef _FORTIFY_SOURCE + #include #include #include From 2a815361448d01b0f4e575f507ce31913214c536 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 9 Apr 2015 13:42:33 -0700 Subject: [PATCH 088/123] Support symbol versioning Bug: http://b/20139821 Change-Id: I64122a0fb0960c20b2ce614161b7ab048456b681 --- libc/include/elf.h | 6 + linker/dlfcn.cpp | 2 +- linker/linked_list.h | 11 + linker/linker.cpp | 405 ++++++++++++++++++++-- linker/linker.h | 64 +++- linker/linker_mips.cpp | 37 +- tests/dlfcn_test.cpp | 60 ++++ tests/libs/Android.build.versioned_lib.mk | 120 +++++++ tests/libs/Android.mk | 6 + tests/libs/versioned_lib_other.cpp | 21 ++ tests/libs/versioned_lib_other.map | 9 + tests/libs/versioned_lib_v1.cpp | 30 ++ tests/libs/versioned_lib_v1.map | 12 + tests/libs/versioned_lib_v2.cpp | 35 ++ tests/libs/versioned_lib_v2.map | 16 + tests/libs/versioned_lib_v3.cpp | 42 +++ tests/libs/versioned_lib_v3.map | 21 ++ tests/libs/versioned_uselib.cpp | 32 ++ tests/libs/versioned_uselib.map | 9 + 19 files changed, 895 insertions(+), 43 deletions(-) create mode 100644 tests/libs/Android.build.versioned_lib.mk create mode 100644 tests/libs/versioned_lib_other.cpp create mode 100644 tests/libs/versioned_lib_other.map create mode 100644 tests/libs/versioned_lib_v1.cpp create mode 100644 tests/libs/versioned_lib_v1.map create mode 100644 tests/libs/versioned_lib_v2.cpp create mode 100644 tests/libs/versioned_lib_v2.map create mode 100644 tests/libs/versioned_lib_v3.cpp create mode 100644 tests/libs/versioned_lib_v3.map create mode 100644 tests/libs/versioned_uselib.cpp create mode 100644 tests/libs/versioned_uselib.map diff --git a/libc/include/elf.h b/libc/include/elf.h index 801d9ff72..df768ba52 100644 --- a/libc/include/elf.h +++ b/libc/include/elf.h @@ -194,4 +194,10 @@ typedef struct { #define NT_GNU_BUILD_ID 3 +#define VER_FLG_BASE 0x1 +#define VER_FLG_WEAK 0x2 + +#define VER_NDX_LOCAL 0 +#define VER_NDX_GLOBAL 1 + #endif /* _ELF_H */ diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 5ed88914c..057c2173c 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -100,7 +100,7 @@ void* dlsym(void* handle, const char* symbol) { } soinfo* found = nullptr; - ElfW(Sym)* sym = nullptr; + const ElfW(Sym)* sym = nullptr; void* caller_addr = __builtin_return_address(0); soinfo* caller = find_containing_library(caller_addr); diff --git a/linker/linked_list.h b/linker/linked_list.h index a72b73ccd..8003dbf84 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h @@ -136,6 +136,17 @@ class LinkedList { } } + template + T* find_if(F predicate) const { + for (LinkedListEntry* e = head_; e != nullptr; e = e->next) { + if (predicate(e->element)) { + return e->element; + } + } + + return nullptr; + } + size_t copy_to_array(T* array[], size_t array_length) const { size_t sz = 0; for (LinkedListEntry* e = head_; sz < array_length && e != nullptr; e = e->next) { diff --git a/linker/linker.cpp b/linker/linker.cpp index 3c8ba76e6..e029dbdbf 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 The Android Open Source Project + * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -100,6 +100,9 @@ static const char* const kDefaultLdPaths[] = { nullptr }; +static const ElfW(Versym) kVersymNotNeeded = 0; +static const ElfW(Versym) kVersymGlobal = 1; + static std::vector g_ld_library_paths; static std::vector g_ld_preload_names; @@ -379,8 +382,128 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void return rv; } -ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) { - return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name); +const ElfW(Versym)* soinfo::get_versym(size_t n) const { + if (has_min_version(2) && versym_ != nullptr) { + return versym_ + n; + } + + return nullptr; +} + +ElfW(Addr) soinfo::get_verneed_ptr() const { + if (has_min_version(2)) { + return verneed_ptr_; + } + + return 0; +} + +size_t soinfo::get_verneed_cnt() const { + if (has_min_version(2)) { + return verneed_cnt_; + } + + return 0; +} + +ElfW(Addr) soinfo::get_verdef_ptr() const { + if (has_min_version(2)) { + return verdef_ptr_; + } + + return 0; +} + +size_t soinfo::get_verdef_cnt() const { + if (has_min_version(2)) { + return verdef_cnt_; + } + + return 0; +} + +template +static bool for_each_verdef(const soinfo* si, F functor) { + if (!si->has_min_version(2)) { + return true; + } + + uintptr_t verdef_ptr = si->get_verdef_ptr(); + if (verdef_ptr == 0) { + return true; + } + + size_t offset = 0; + + size_t verdef_cnt = si->get_verdef_cnt(); + for (size_t i = 0; i(verdef_ptr + offset); + size_t verdaux_offset = offset + verdef->vd_aux; + offset += verdef->vd_next; + + if (verdef->vd_version != 1) { + DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1)", i, verdef->vd_version); + return false; + } + + if ((verdef->vd_flags & VER_FLG_BASE) != 0) { + // "this is the version of the file itself. It must not be used for + // matching a symbol. It can be used to match references." + // + // http://www.akkadia.org/drepper/symbol-versioning + continue; + } + + if (verdef->vd_cnt == 0) { + DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i); + return false; + } + + const ElfW(Verdaux)* verdaux = reinterpret_cast(verdef_ptr + verdaux_offset); + + if (functor(i, verdef, verdaux) == true) { + break; + } + } + + return true; +} + +bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const { + if (vi == nullptr) { + *versym = kVersymNotNeeded; + return true; + } + + *versym = kVersymGlobal; + + return for_each_verdef(this, + [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { + if (verdef->vd_hash == vi->elf_hash && + strcmp(vi->name, get_string(verdaux->vda_name)) == 0) { + *versym = verdef->vd_ndx; + return true; + } + + return false; + } + ); +} + +bool soinfo::find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi, + const ElfW(Sym)** symbol) const { + uint32_t symbol_index; + bool success = + is_gnu_hash() ? + gnu_lookup(symbol_name, vi, &symbol_index) : + elf_lookup(symbol_name, vi, &symbol_index); + + if (success) { + *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index; + } + + return success; } static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { @@ -395,7 +518,23 @@ static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { return false; } -ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { +static const ElfW(Versym) kVersymHiddenBit = 0x8000; + +static inline bool is_versym_hidden(const ElfW(Versym)* versym) { + // the symbol is hidden if bit 15 of versym is set. + return versym != nullptr && (*versym & kVersymHiddenBit) != 0; +} + +static inline bool check_symbol_version(const ElfW(Versym) verneed, + const ElfW(Versym)* verdef) { + return verneed == kVersymNotNeeded || + verdef == nullptr || + verneed == (*verdef & ~kVersymHiddenBit); +} + +bool soinfo::gnu_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) const { uint32_t hash = symbol_name.gnu_hash(); uint32_t h2 = hash >> gnu_shift2_; @@ -403,6 +542,8 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_; ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num]; + *symbol_index = 0; + TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)", symbol_name.get_name(), get_soname(), reinterpret_cast(base)); @@ -411,7 +552,7 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_soname(), reinterpret_cast(base)); - return nullptr; + return true; } // bloom test says "probably yes"... @@ -421,43 +562,77 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_soname(), reinterpret_cast(base)); - return nullptr; + return true; + } + + // lookup versym for the version definition in this library + // note the difference between "version is not requested" (vi == nullptr) + // and "version not found". In the first case verneed is kVersymNotNeeded + // which implies that the default version can be accepted; the second case results in + // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols + // for this library and consider only *global* ones. + ElfW(Versym) verneed = 0; + if (!find_verdef_version_index(vi, &verneed)) { + return false; } do { ElfW(Sym)* s = symtab_ + n; + const ElfW(Versym)* verdef = get_versym(n); + // skip hidden versions when verneed == kVersymNotNeeded (0) + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } if (((gnu_chain_[n] ^ hash) >> 1) == 0 && + check_symbol_version(verneed, verdef) && strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", symbol_name.get_name(), get_soname(), reinterpret_cast(s->st_value), static_cast(s->st_size)); - return s; + *symbol_index = n; + return true; } } while ((gnu_chain_[n++] & 1) == 0); TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_soname(), reinterpret_cast(base)); - return nullptr; + return true; } -ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { +bool soinfo::elf_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) const { uint32_t hash = symbol_name.elf_hash(); TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd", symbol_name.get_name(), get_soname(), reinterpret_cast(base), hash, hash % nbucket_); + ElfW(Versym) verneed = 0; + if (!find_verdef_version_index(vi, &verneed)) { + return false; + } + for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { ElfW(Sym)* s = symtab_ + n; - if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + const ElfW(Versym)* verdef = get_versym(n); + + // skip hidden versions when verneed == 0 + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } + + if (check_symbol_version(verneed, verdef) && + strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", symbol_name.get_name(), get_soname(), reinterpret_cast(s->st_value), static_cast(s->st_size)); - return s; + *symbol_index = n; + return true; } } @@ -465,7 +640,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { symbol_name.get_name(), get_soname(), reinterpret_cast(base), hash, hash % nbucket_); - return nullptr; + *symbol_index = 0; + return true; } soinfo::soinfo(const char* realpath, const struct stat* file_stat, @@ -523,10 +699,11 @@ uint32_t SymbolName::gnu_hash() { return gnu_hash_; } -ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, - const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { +bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, + soinfo** si_found_in, const soinfo::soinfo_list_t& global_group, + const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) { SymbolName symbol_name(name); - ElfW(Sym)* s = nullptr; + const ElfW(Sym)* s = nullptr; /* "This element's presence in a shared object library alters the dynamic linker's * symbol resolution algorithm for references within the library. Instead of starting @@ -541,7 +718,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found */ if (si_from->has_DT_SYMBOLIC) { DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name); - s = si_from->find_symbol_by_name(symbol_name); + if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) { + return false; + } + if (s != nullptr) { *si_found_in = si_from; } @@ -549,10 +729,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found // 1. Look for it in global_group if (s == nullptr) { + bool error = false; global_group.visit([&](soinfo* global_si) { DEBUG("%s: looking up %s in %s (from global group)", si_from->get_soname(), name, global_si->get_soname()); - s = global_si->find_symbol_by_name(symbol_name); + if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) { + error = true; + return false; + } + if (s != nullptr) { *si_found_in = global_si; return false; @@ -560,10 +745,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found return true; }); + + if (error) { + return false; + } } // 2. Look for it in the local group if (s == nullptr) { + bool error = false; local_group.visit([&](soinfo* local_si) { if (local_si == si_from && si_from->has_DT_SYMBOLIC) { // we already did this - skip @@ -572,7 +762,11 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found DEBUG("%s: looking up %s in %s (from local group)", si_from->get_soname(), name, local_si->get_soname()); - s = local_si->find_symbol_by_name(symbol_name); + if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) { + error = true; + return false; + } + if (s != nullptr) { *si_found_in = local_si; return false; @@ -580,6 +774,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found return true; }); + + if (error) { + return false; + } } if (s != nullptr) { @@ -590,7 +788,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found reinterpret_cast((*si_found_in)->load_bias)); } - return s; + *symbol = s; + return true; } class ProtectedDataGuard { @@ -735,13 +934,16 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s // This is used by dlsym(3). It performs symbol lookup only within the // specified soinfo object and its dependencies in breadth first order. -ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { - ElfW(Sym)* result = nullptr; +const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { + const ElfW(Sym)* result = nullptr; SymbolName symbol_name(name); - walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) { - result = current_soinfo->find_symbol_by_name(symbol_name); + if (!current_soinfo->find_symbol_by_name(symbol_name, nullptr, &result)) { + result = nullptr; + return false; + } + if (result != nullptr) { *found = current_soinfo; return false; @@ -758,7 +960,10 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { beginning of the global solist. Otherwise the search starts at the specified soinfo (for RTLD_NEXT). */ -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle) { +const ElfW(Sym)* dlsym_linear_lookup(const char* name, + soinfo** found, + soinfo* caller, + void* handle) { SymbolName symbol_name(name); soinfo* start = solist; @@ -771,13 +976,16 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, } } - ElfW(Sym)* s = nullptr; + const ElfW(Sym)* s = nullptr; for (soinfo* si = start; si != nullptr; si = si->next) { if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) { continue; } - s = si->find_symbol_by_name(symbol_name); + if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) { + return nullptr; + } + if (s != nullptr) { *found = si; break; @@ -800,7 +1008,10 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, break; } - s = si->find_symbol_by_name(symbol_name); + if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) { + return nullptr; + } + if (s != nullptr) { *found = si; break; @@ -1444,6 +1655,93 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { return ifunc_addr; } +const version_info* VersionTracker::get_version_info(ElfW(Versym) source_symver) const { + if (source_symver < 2 || + source_symver >= version_infos.size() || + version_infos[source_symver].name == nullptr) { + return nullptr; + } + + return &version_infos[source_symver]; +} + +void VersionTracker::add_version_info(size_t source_index, + ElfW(Word) elf_hash, + const char* ver_name, + const soinfo* target_si) { + if (source_index >= version_infos.size()) { + version_infos.resize(source_index+1); + } + + version_infos[source_index].elf_hash = elf_hash; + version_infos[source_index].name = ver_name; + version_infos[source_index].target_si = target_si; +} + +bool VersionTracker::init_verneed(const soinfo* si_from) { + uintptr_t verneed_ptr = si_from->get_verneed_ptr(); + + if (verneed_ptr == 0) { + return true; + } + + size_t verneed_cnt = si_from->get_verneed_cnt(); + + for (size_t i = 0, offset = 0; i(verneed_ptr + offset); + size_t vernaux_offset = offset + verneed->vn_aux; + offset += verneed->vn_next; + + if (verneed->vn_version != 1) { + DL_ERR("unsupported verneed[%zd] vn_version: %d (expected 1)", i, verneed->vn_version); + return false; + } + + const char* target_soname = si_from->get_string(verneed->vn_file); + // find it in dependencies + soinfo* target_si = si_from->get_children().find_if([&](const soinfo* si) { + return strcmp(si->get_soname(), target_soname) == 0; + }); + + if (target_si == nullptr) { + DL_ERR("cannot find \"%s\" from verneed[%zd] in DT_NEEDED list for \"%s\"", + target_soname, i, si_from->get_soname()); + return false; + } + + for (size_t j = 0; jvn_cnt; ++j) { + const ElfW(Vernaux)* vernaux = reinterpret_cast(verneed_ptr + vernaux_offset); + vernaux_offset += vernaux->vna_next; + + const ElfW(Word) elf_hash = vernaux->vna_hash; + const char* ver_name = si_from->get_string(vernaux->vna_name); + ElfW(Half) source_index = vernaux->vna_other; + + add_version_info(source_index, elf_hash, ver_name, target_si); + } + } + + return true; +} + +bool VersionTracker::init_verdef(const soinfo* si_from) { + return for_each_verdef(si_from, + [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { + add_version_info(verdef->vd_ndx, verdef->vd_hash, + si_from->get_string(verdaux->vda_name), si_from); + return false; + } + ); +} + +bool VersionTracker::init(const soinfo* si_from) { + if (!si_from->has_min_version(2)) { + return true; + } + + return init_verneed(si_from) && init_verdef(si_from); +} + #if !defined(__mips__) #if defined(USE_RELA) static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { @@ -1462,6 +1760,12 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { template bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + VersionTracker version_tracker; + + if (!version_tracker.init(this)) { + return false; + } + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); if (rel == nullptr) { @@ -1481,12 +1785,32 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa continue; } - ElfW(Sym)* s = nullptr; + const ElfW(Sym)* s = nullptr; soinfo* lsi = nullptr; if (sym != 0) { sym_name = get_string(symtab_[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group); + const ElfW(Versym)* sym_ver_ptr = get_versym(sym); + ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr; + + if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) { + // there is no version info for this one + if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) { + return false; + } + } else { + const version_info* vi = version_tracker.get_version_info(sym_ver); + + if (vi == nullptr) { + DL_ERR("cannot find verneed/verdef for version index=%d " + "referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname()); + return false; + } + + if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) { + return false; + } + } if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference... s = &symtab_[sym]; @@ -1977,6 +2301,14 @@ soinfo::soinfo_list_t& soinfo::get_children() { return g_empty_list; } +const soinfo::soinfo_list_t& soinfo::get_children() const { + if (has_min_version(0)) { + return children_; + } + + return g_empty_list; +} + soinfo::soinfo_list_t& soinfo::get_parents() { if (has_min_version(0)) { return parents_; @@ -1985,7 +2317,7 @@ soinfo::soinfo_list_t& soinfo::get_parents() { return g_empty_list; } -ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) { +ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { return call_ifunc_resolver(s->st_value + load_bias); } @@ -2452,12 +2784,23 @@ bool soinfo::prelink_image() { case DT_BIND_NOW: break; - // Ignore: bionic does not support symbol versioning... case DT_VERSYM: + versym_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + break; + case DT_VERDEF: + verdef_ptr_ = load_bias + d->d_un.d_ptr; + break; case DT_VERDEFNUM: + verdef_cnt_ = d->d_un.d_val; + break; + case DT_VERNEED: + verneed_ptr_ = load_bias + d->d_un.d_ptr; + break; + case DT_VERNEEDNUM: + verneed_cnt_ = d->d_un.d_val; break; default: diff --git a/linker/linker.h b/linker/linker.h index 7482581d8..dae3972c0 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -40,6 +40,7 @@ #include "linked_list.h" #include +#include #define DL_ERR(fmt, x...) \ do { \ @@ -142,6 +143,32 @@ class SymbolName { DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName); }; +struct version_info { + version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {} + + uint32_t elf_hash; + const char* name; + const soinfo* target_si; +}; + +// Class used construct version dependency graph. +class VersionTracker { + public: + VersionTracker() = default; + bool init(const soinfo* si_from); + + const version_info* get_version_info(ElfW(Versym) source_symver) const; + private: + bool init_verneed(const soinfo* si_from); + bool init_verdef(const soinfo* si_from); + void add_version_info(size_t source_index, ElfW(Word) elf_hash, + const char* ver_name, const soinfo* target_si); + + std::vector version_infos; + + DISALLOW_COPY_AND_ASSIGN(VersionTracker); +}; + struct soinfo { public: typedef LinkedList soinfo_list_t; @@ -260,11 +287,16 @@ struct soinfo { void set_dt_flags_1(uint32_t dt_flags_1); soinfo_list_t& get_children(); + const soinfo_list_t& get_children() const; + soinfo_list_t& get_parents(); - ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name); + bool find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi, + const ElfW(Sym)** symbol) const; + ElfW(Sym)* find_symbol_by_address(const void* addr); - ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s); + ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const; const char* get_string(ElfW(Word) index) const; bool can_unload() const; @@ -292,11 +324,18 @@ struct soinfo { const char* get_soname() const; const char* get_realpath() const; + const ElfW(Versym)* get_versym(size_t n) const; + ElfW(Addr) get_verneed_ptr() const; + size_t get_verneed_cnt() const; + ElfW(Addr) get_verdef_ptr() const; + size_t get_verdef_cnt() const; + + bool find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const; private: - ElfW(Sym)* elf_lookup(SymbolName& symbol_name); + bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; ElfW(Sym)* elf_addr_lookup(const void* addr); - ElfW(Sym)* gnu_lookup(SymbolName& symbol_name); + bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; ElfW(Sym)* gnu_addr_lookup(const void* addr); void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse); @@ -341,11 +380,20 @@ struct soinfo { const char* soname_; std::string realpath_; + const ElfW(Versym)* versym_; + + ElfW(Addr) verdef_ptr_; + size_t verdef_cnt_; + + ElfW(Addr) verneed_ptr_; + size_t verneed_cnt_; + friend soinfo* get_libdl_info(); }; -ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, - const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group); +bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, + soinfo** si_found_in, const soinfo::soinfo_list_t& global_group, + const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol); enum RelocationKind { kRelocAbsolute = 0, @@ -364,10 +412,10 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo); void do_dlclose(soinfo* si); -ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle); +const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle); soinfo* find_containing_library(const void* addr); -ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); +const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); void debuggerd_init(); extern "C" abort_msg_t* g_abort_message; diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index 14f6a1bfb..c162111e9 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -50,6 +50,12 @@ template bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + VersionTracker version_tracker; + + if (!version_tracker.init(this)) { + return false; + } + for (size_t idx = 0; rel_iterator.has_next(); ++idx) { const auto rel = rel_iterator.next(); @@ -69,12 +75,33 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, continue; } - ElfW(Sym)* s = nullptr; + const ElfW(Sym)* s = nullptr; soinfo* lsi = nullptr; if (sym != 0) { sym_name = get_string(symtab_[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group); + const ElfW(Versym)* sym_ver_ptr = get_versym(sym); + ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr; + + if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) { + // there is no version info for this one + if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) { + return false; + } + } else { + const version_info* vi = version_tracker.get_version_info(sym_ver); + + if (vi == nullptr) { + DL_ERR("cannot find verneed/verdef for version index=%d " + "referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname()); + return false; + } + + if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) { + return false; + } + } + if (s == nullptr) { // mips does not support relocation with weak-undefined symbols DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname()); @@ -147,7 +174,11 @@ bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, // This is an undefined reference... try to locate it. const char* sym_name = get_string(sym->st_name); soinfo* lsi = nullptr; - ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group); + const ElfW(Sym)* s = nullptr; + if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) { + return false; + } + if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference. s = &symtab_[g]; diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 708e2cd5f..1023644c0 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -925,3 +925,63 @@ TEST(dlfcn, dlopen_dlopen_from_ctor) { GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n"; #endif } + +TEST(dlfcn, symbol_versioning_use_v1) { + void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "get_function_version")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(1, fn()); + dlclose(handle); +} + +TEST(dlfcn, symbol_versioning_use_v2) { + void* handle = dlopen("libtest_versioned_uselibv2.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "get_function_version")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(2, fn()); + dlclose(handle); +} + +TEST(dlfcn, symbol_versioning_use_other_v2) { + void* handle = dlopen("libtest_versioned_uselibv2_other.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "get_function_version")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(20, fn()); + dlclose(handle); +} + +TEST(dlfcn, symbol_versioning_use_other_v3) { + void* handle = dlopen("libtest_versioned_uselibv3_other.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "get_function_version")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(3, fn()); + dlclose(handle); +} + +TEST(dlfcn, symbol_versioning_default_via_dlsym) { + void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(); + fn_t fn = reinterpret_cast(dlsym(handle, "versioned_function")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + ASSERT_EQ(3, fn()); // the default version is 3 + dlclose(handle); +} + +// This preempts the implementation from libtest_versioned_lib.so +extern "C" int version_zero_function() { + return 0; +} + +// This preempts the implementation from libtest_versioned_uselibv*.so +extern "C" int version_zero_function2() { + return 0; +} diff --git a/tests/libs/Android.build.versioned_lib.mk b/tests/libs/Android.build.versioned_lib.mk new file mode 100644 index 000000000..f3a637424 --- /dev/null +++ b/tests/libs/Android.build.versioned_lib.mk @@ -0,0 +1,120 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ----------------------------------------------------------------------------- +# Libraries used to test versioned symbols +# ----------------------------------------------------------------------------- +libtest_versioned_uselibv1_src_files := versioned_uselib.cpp + +libtest_versioned_uselibv1_shared_libraries := \ + libtest_versioned_libv1 + +module := libtest_versioned_uselibv1 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +libtest_versioned_uselibv2_src_files := \ + versioned_uselib.cpp + +libtest_versioned_uselibv2_shared_libraries := \ + libtest_versioned_libv2 + +libtest_versioned_uselibv2_ldflags := \ + -Wl,--version-script,$(LOCAL_PATH)/versioned_uselib.map + +module := libtest_versioned_uselibv2 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +libtest_versioned_uselibv2_other_src_files := \ + versioned_uselib.cpp + +libtest_versioned_uselibv2_other_shared_libraries := \ + libtest_versioned_otherlib_empty libtest_versioned_libv2 + +module := libtest_versioned_uselibv2_other +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +libtest_versioned_uselibv3_other_src_files := \ + versioned_uselib.cpp + +libtest_versioned_uselibv3_other_shared_libraries := \ + libtest_versioned_otherlib_empty libtest_versioned_lib + +module := libtest_versioned_uselibv3_other +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# lib v1 - this one used during static linking but never used at runtime +# which forces libtest_versioned_uselibv1 to use function v1 from +# libtest_versioned_lib.so +# ----------------------------------------------------------------------------- +libtest_versioned_libv1_src_files := \ + versioned_lib_v1.cpp + +libtest_versioned_libv1_ldflags := \ + -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v1.map \ + -Wl,-soname,libtest_versioned_lib.so + +module := libtest_versioned_libv1 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# lib v2 - to make libtest_versioned_uselibv2.so use version 2 of versioned_function() +# ----------------------------------------------------------------------------- +libtest_versioned_libv2_src_files := \ + versioned_lib_v2.cpp + +libtest_versioned_libv2_ldflags := \ + -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v2.map \ + -Wl,-soname,libtest_versioned_lib.so + +module := libtest_versioned_libv2 +include $(LOCAL_PATH)/Android.build.testlib.mk + + +# ----------------------------------------------------------------------------- +# last version - this one is used at the runtime and exports 3 versions +# of versioned_symbol(). +# ----------------------------------------------------------------------------- +libtest_versioned_lib_src_files := \ + versioned_lib_v3.cpp + +libtest_versioned_lib_ldflags := \ + -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v3.map + +module := libtest_versioned_lib +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# This library is empty, the actual implementation will provide an unversioned +# symbol for versioned_function(). +# ----------------------------------------------------------------------------- +libtest_versioned_otherlib_empty_src_files := empty.cpp + +libtest_versioned_otherlib_empty_ldflags := -Wl,-soname,libtest_versioned_otherlib.so +module := libtest_versioned_otherlib_empty +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +libtest_versioned_otherlib_src_files := versioned_lib_other.cpp + +libtest_versioned_otherlib_ldflags := \ + -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_other.map + +module := libtest_versioned_otherlib +include $(LOCAL_PATH)/Android.build.testlib.mk diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index da3fb1e46..3d5b060de 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -26,6 +26,7 @@ common_additional_dependencies := \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ + $(LOCAL_PATH)/Android.build.versioned_lib.mk \ $(TEST_PATH)/Android.build.mk # ----------------------------------------------------------------------------- @@ -197,6 +198,11 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk # ----------------------------------------------------------------------------- include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk +# ----------------------------------------------------------------------------- +# Build libtest_versioned_lib.so with its dependencies. +# ----------------------------------------------------------------------------- +include $(LOCAL_PATH)/Android.build.versioned_lib.mk + # ----------------------------------------------------------------------------- # Library with dependency loop used by dlfcn tests # diff --git a/tests/libs/versioned_lib_other.cpp b/tests/libs/versioned_lib_other.cpp new file mode 100644 index 000000000..60fa99abb --- /dev/null +++ b/tests/libs/versioned_lib_other.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" int versioned_function_v2() { + return 20; +} + +__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2"); diff --git a/tests/libs/versioned_lib_other.map b/tests/libs/versioned_lib_other.map new file mode 100644 index 000000000..752686def --- /dev/null +++ b/tests/libs/versioned_lib_other.map @@ -0,0 +1,9 @@ +TESTLIB_V0 { + local: + versioned_function_v*; +}; + +TESTLIB_V2 { + global: + versioned_function; +} TESTLIB_V0; diff --git a/tests/libs/versioned_lib_v1.cpp b/tests/libs/versioned_lib_v1.cpp new file mode 100644 index 000000000..c81cbf1ea --- /dev/null +++ b/tests/libs/versioned_lib_v1.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" { + int versioned_function_v1(); // __attribute__((visibility("hidden"))); + int version_zero_function(); +} + +int versioned_function_v1() { + return 1; +} + +int version_zero_function() { + return 100; +} + +__asm__(".symver versioned_function_v1,versioned_function@@TESTLIB_V1"); diff --git a/tests/libs/versioned_lib_v1.map b/tests/libs/versioned_lib_v1.map new file mode 100644 index 000000000..dbda3276c --- /dev/null +++ b/tests/libs/versioned_lib_v1.map @@ -0,0 +1,12 @@ +TESTLIB_V0 { + global: + version_zero_function; + local: + versioned_function_v*; +}; + +TESTLIB_V1 { + global: + versioned_function; +} TESTLIB_V0; + diff --git a/tests/libs/versioned_lib_v2.cpp b/tests/libs/versioned_lib_v2.cpp new file mode 100644 index 000000000..d7d413f36 --- /dev/null +++ b/tests/libs/versioned_lib_v2.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" { + int versioned_function_v1(); // __attribute__((visibility("hidden"))); + int versioned_function_v2(); // __attribute__((visibility("hidden"))); + int version_zero_function(); +} + +int versioned_function_v1() { + return 1; +} + +int versioned_function_v2() { + return 2; +} + +int version_zero_function() { + return 200; +} +__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1"); +__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2"); diff --git a/tests/libs/versioned_lib_v2.map b/tests/libs/versioned_lib_v2.map new file mode 100644 index 000000000..bb381029d --- /dev/null +++ b/tests/libs/versioned_lib_v2.map @@ -0,0 +1,16 @@ +TESTLIB_V0 { + global: + version_zero_function; + local: + versioned_function_v*; +}; + +TESTLIB_V1 { + global: + versioned_function; +} TESTLIB_V0; + +TESTLIB_V2 { + global: + versioned_function; +} TESTLIB_V1; diff --git a/tests/libs/versioned_lib_v3.cpp b/tests/libs/versioned_lib_v3.cpp new file mode 100644 index 000000000..f4740a46e --- /dev/null +++ b/tests/libs/versioned_lib_v3.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" { + int versioned_function_v1(); // __attribute__((visibility("hidden"))); + int versioned_function_v2(); // __attribute__((visibility("hidden"))); + int versioned_function_v3(); // __attribute__((visibility("hidden"))); + int version_zero_function(); +} + +int versioned_function_v1() { + return 1; +} + +int versioned_function_v2() { + return 2; +} + +int versioned_function_v3() { + return 3; +} + +int version_zero_function() { + return 1000; +} + +__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1"); +__asm__(".symver versioned_function_v2,versioned_function@TESTLIB_V2"); +__asm__(".symver versioned_function_v3,versioned_function@@TESTLIB_V3"); diff --git a/tests/libs/versioned_lib_v3.map b/tests/libs/versioned_lib_v3.map new file mode 100644 index 000000000..5b1ce5983 --- /dev/null +++ b/tests/libs/versioned_lib_v3.map @@ -0,0 +1,21 @@ +TESTLIB_V0 { + global: + version_zero_function; + local: + versioned_function_v*; +}; + +TESTLIB_V1 { + global: + versioned_function; +} TESTLIB_V0; + +TESTLIB_V2 { + global: + versioned_function; +} TESTLIB_V1; + +TESTLIB_V3 { + global: + versioned_function; +} TESTLIB_V2; diff --git a/tests/libs/versioned_uselib.cpp b/tests/libs/versioned_uselib.cpp new file mode 100644 index 000000000..96eb7c3b4 --- /dev/null +++ b/tests/libs/versioned_uselib.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern "C" { + int versioned_function(); + + int get_function_version(); + int version_zero_function(); + int version_zero_function2() __attribute__((weak)); +} + +int get_function_version() { + return version_zero_function2() + version_zero_function() + versioned_function(); +} + +// we expect this function to be preempted by main executable. +int version_zero_function2() { + return 40000; +} diff --git a/tests/libs/versioned_uselib.map b/tests/libs/versioned_uselib.map new file mode 100644 index 000000000..10bc9ced5 --- /dev/null +++ b/tests/libs/versioned_uselib.map @@ -0,0 +1,9 @@ +TESTLIB_NONE { + global: + get_function_version; +}; + +TESTLIB_ZERO { + global: + version_zero_function2; +} TESTLIB_NONE; From 42281880a8ac8614832ff918a14e4b950f35d05d Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Fri, 17 Apr 2015 11:26:36 -0400 Subject: [PATCH 089/123] add fortified readlink/readlinkat implementations Change-Id: Ia4b1824d20cad3a072b9162047492dade8576779 --- libc/Android.mk | 2 ++ libc/bionic/__readlink_chk.cpp | 43 +++++++++++++++++++++++ libc/bionic/__readlinkat_chk.cpp | 43 +++++++++++++++++++++++ libc/bionic/readlink.cpp | 2 ++ libc/include/unistd.h | 60 ++++++++++++++++++++++++++++++++ tests/fortify_test.cpp | 12 +++++++ 6 files changed, 162 insertions(+) create mode 100644 libc/bionic/__readlink_chk.cpp create mode 100644 libc/bionic/__readlinkat_chk.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 9d3b62aeb..814940cb1 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -74,6 +74,8 @@ libc_common_src_files += \ bionic/__pread64_chk.cpp \ bionic/__pread_chk.cpp \ bionic/__read_chk.cpp \ + bionic/__readlink_chk.cpp \ + bionic/__readlinkat_chk.cpp \ bionic/__recvfrom_chk.cpp \ bionic/__stpcpy_chk.cpp \ bionic/__stpncpy_chk.cpp \ diff --git a/libc/bionic/__readlink_chk.cpp b/libc/bionic/__readlink_chk.cpp new file mode 100644 index 000000000..f19f917ea --- /dev/null +++ b/libc/bionic/__readlink_chk.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" ssize_t __readlink_chk(const char* path, char* buf, size_t size, size_t buf_size) { + if (__predict_false(size > buf_size)) { + __fortify_chk_fail("readlink: prevented write past end of buffer", 0); + } + + if (__predict_false(size > SSIZE_MAX)) { + __fortify_chk_fail("readlink: size > SSIZE_MAX", 0); + } + + return readlink(path, buf, size); +} diff --git a/libc/bionic/__readlinkat_chk.cpp b/libc/bionic/__readlinkat_chk.cpp new file mode 100644 index 000000000..a11db8eed --- /dev/null +++ b/libc/bionic/__readlinkat_chk.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" ssize_t __readlinkat_chk(int dirfd, const char* path, char* buf, size_t size, size_t buf_size) { + if (__predict_false(size > buf_size)) { + __fortify_chk_fail("readlinkat: prevented write past end of buffer", 0); + } + + if (__predict_false(size > SSIZE_MAX)) { + __fortify_chk_fail("readlinkat: size > SSIZE_MAX", 0); + } + + return readlinkat(dirfd, path, buf, size); +} diff --git a/libc/bionic/readlink.cpp b/libc/bionic/readlink.cpp index 3bb7bc18a..a53f933c8 100644 --- a/libc/bionic/readlink.cpp +++ b/libc/bionic/readlink.cpp @@ -26,6 +26,8 @@ * SUCH DAMAGE. */ +#undef _FORTIFY_SOURCE + #include #include #include diff --git a/libc/include/unistd.h b/libc/include/unistd.h index a601cb79a..f0de29e2a 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -239,6 +239,16 @@ __errordecl(__read_dest_size_error, "read called with size bigger than destinati __errordecl(__read_count_toobig_error, "read called with count > SSIZE_MAX"); extern ssize_t __read_real(int, void*, size_t) __RENAME(read); +extern ssize_t __readlink_chk(const char*, char*, size_t, size_t); +__errordecl(__readlink_dest_size_error, "readlink called with size bigger than destination"); +__errordecl(__readlink_size_toobig_error, "readlink called with size > SSIZE_MAX"); +extern ssize_t __readlink_real(const char*, char*, size_t) __RENAME(readlink); + +extern ssize_t __readlinkat_chk(int dirfd, const char*, char*, size_t, size_t); +__errordecl(__readlinkat_dest_size_error, "readlinkat called with size bigger than destination"); +__errordecl(__readlinkat_size_toobig_error, "readlinkat called with size > SSIZE_MAX"); +extern ssize_t __readlinkat_real(int dirfd, const char*, char*, size_t) __RENAME(readlinkat); + #if defined(__BIONIC_FORTIFY) #if defined(__USE_FILE_OFFSET64) @@ -322,6 +332,56 @@ ssize_t read(int fd, void* buf, size_t count) { return __read_chk(fd, buf, count, bos); } +__BIONIC_FORTIFY_INLINE +ssize_t readlink(const char* path, char* buf, size_t size) { + size_t bos = __bos(buf); + +#if !defined(__clang__) + if (__builtin_constant_p(size) && (size > SSIZE_MAX)) { + __readlink_size_toobig_error(); + } + + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __readlink_real(path, buf, size); + } + + if (__builtin_constant_p(size) && (size > bos)) { + __readlink_dest_size_error(); + } + + if (__builtin_constant_p(size) && (size <= bos)) { + return __readlink_real(path, buf, size); + } +#endif + + return __readlink_chk(path, buf, size, bos); +} + +__BIONIC_FORTIFY_INLINE +ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t size) { + size_t bos = __bos(buf); + +#if !defined(__clang__) + if (__builtin_constant_p(size) && (size > SSIZE_MAX)) { + __readlinkat_size_toobig_error(); + } + + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __readlinkat_real(dirfd, path, buf, size); + } + + if (__builtin_constant_p(size) && (size > bos)) { + __readlinkat_dest_size_error(); + } + + if (__builtin_constant_p(size) && (size <= bos)) { + return __readlinkat_real(dirfd, path, buf, size); + } +#endif + + return __readlinkat_chk(dirfd, path, buf, size, bos); +} + #endif /* defined(__BIONIC_FORTIFY) */ __END_DECLS diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp index 70159de3c..4faccb49f 100644 --- a/tests/fortify_test.cpp +++ b/tests/fortify_test.cpp @@ -647,6 +647,18 @@ TEST_F(DEATHTEST, read_fortified) { close(fd); } +TEST_F(DEATHTEST, readlink_fortified) { + char buf[1]; + size_t ct = atoi("2"); // prevent optimizations + ASSERT_FORTIFY(readlink("/dev/null", buf, ct)); +} + +TEST_F(DEATHTEST, readlinkat_fortified) { + char buf[1]; + size_t ct = atoi("2"); // prevent optimizations + ASSERT_FORTIFY(readlinkat(AT_FDCWD, "/dev/null", buf, ct)); +} + extern "C" char* __strncat_chk(char*, const char*, size_t, size_t); extern "C" char* __strcat_chk(char*, const char*, size_t); From d3fe4f1229ae0a50276b02d9c8e1efe8949a8726 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 17 Apr 2015 13:01:29 -0700 Subject: [PATCH 090/123] Merge the two bionicbb services into one. Change-Id: I6490da1ec96b2e24b330296950be84424e11bd35 --- tools/bionicbb/README.md | 1 + .../{build_listener.py => bionicbb.py} | 14 +- tools/bionicbb/gmail.py | 71 ++++++++ .../{gmail_listener.py => presubmit.py} | 161 +----------------- tools/bionicbb/tasks.py | 108 ++++++++++++ .../{test_gmail_listener.py => test_tasks.py} | 33 ++-- 6 files changed, 215 insertions(+), 173 deletions(-) rename tools/bionicbb/{build_listener.py => bionicbb.py} (91%) create mode 100644 tools/bionicbb/gmail.py rename tools/bionicbb/{gmail_listener.py => presubmit.py} (58%) create mode 100644 tools/bionicbb/tasks.py rename tools/bionicbb/{test_gmail_listener.py => test_tasks.py} (74%) diff --git a/tools/bionicbb/README.md b/tools/bionicbb/README.md index 4d3291fc3..a28598484 100644 --- a/tools/bionicbb/README.md +++ b/tools/bionicbb/README.md @@ -8,6 +8,7 @@ Dependencies ------------ * Python 2.7 + * [Advanced Python Scheduler](https://apscheduler.readthedocs.org/en/latest/) * [Flask](http://flask.pocoo.org/) * [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation) * [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi) diff --git a/tools/bionicbb/build_listener.py b/tools/bionicbb/bionicbb.py similarity index 91% rename from tools/bionicbb/build_listener.py rename to tools/bionicbb/bionicbb.py index fa55d37d6..20d6460ed 100644 --- a/tools/bionicbb/build_listener.py +++ b/tools/bionicbb/bionicbb.py @@ -16,11 +16,15 @@ # import json import logging +import os + +from apscheduler.schedulers.background import BackgroundScheduler +from flask import Flask, request import requests import gerrit +import tasks -from flask import Flask, request app = Flask(__name__) @@ -115,4 +119,12 @@ def drop_rejection(): if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + # Prevent the job from being rescheduled by the reloader. + if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + scheduler = BackgroundScheduler() + scheduler.start() + scheduler.add_job(tasks.get_and_process_jobs, 'interval', minutes=5) + app.run(host='0.0.0.0', debug=True) diff --git a/tools/bionicbb/gmail.py b/tools/bionicbb/gmail.py new file mode 100644 index 000000000..f088ad63a --- /dev/null +++ b/tools/bionicbb/gmail.py @@ -0,0 +1,71 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import base64 +import httplib2 + +import config + + +def get_body(msg): + if 'attachmentId' in msg['payload']['body']: + raise NotImplementedError('Handling of messages contained in ' + 'attachments not yet implemented.') + b64_body = msg['payload']['body']['data'] + return base64.urlsafe_b64decode(b64_body.encode('ASCII')) + + +def build_service(): + from apiclient.discovery import build + from oauth2client.client import flow_from_clientsecrets + from oauth2client.file import Storage + from oauth2client.tools import run + + OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.modify' + STORAGE = Storage('oauth.storage') + + # Start the OAuth flow to retrieve credentials + flow = flow_from_clientsecrets(config.client_secret_file, + scope=OAUTH_SCOPE) + http = httplib2.Http() + + # Try to retrieve credentials from storage or run the flow to generate them + credentials = STORAGE.get() + if credentials is None or credentials.invalid: + credentials = run(flow, STORAGE, http=http) + + http = credentials.authorize(http) + return build('gmail', 'v1', http=http) + + +def get_gerrit_label(labels): + for label in labels: + if label['name'] == 'gerrit': + return label['id'] + return None + + +def get_all_messages(service, label): + msgs = [] + response = service.users().messages().list( + userId='me', labelIds=label).execute() + if 'messages' in response: + msgs.extend(response['messages']) + while 'nextPageToken' in response: + page_token = response['nextPageToken'] + response = service.users().messages().list( + userId='me', pageToken=page_token).execute() + msgs.extend(response['messages']) + return msgs diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/presubmit.py similarity index 58% rename from tools/bionicbb/gmail_listener.py rename to tools/bionicbb/presubmit.py index 134258a78..cc6f3ccd8 100644 --- a/tools/bionicbb/gmail_listener.py +++ b/tools/bionicbb/presubmit.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # # Copyright (C) 2015 The Android Open Source Project # @@ -14,42 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import base64 -import httplib -import httplib2 -import jenkinsapi +from __future__ import absolute_import + import json import logging -import os +import os.path import re import requests -import socket -import sys -import time -import apiclient.errors +import jenkinsapi -import config import gerrit - -class GmailError(RuntimeError): - def __init__(self, message): - super(GmailError, self).__init__(message) - - -def get_gerrit_label(labels): - for label in labels: - if label['name'] == 'gerrit': - return label['id'] - return None - - -def get_headers(msg): - headers = {} - for hdr in msg['payload']['headers']: - headers[hdr['name']] = hdr['value'] - return headers +import config def is_untrusted_committer(change_id, patch_set): @@ -88,59 +64,6 @@ def should_skip_build(info): return False -def build_service(): - from apiclient.discovery import build - from oauth2client.client import flow_from_clientsecrets - from oauth2client.file import Storage - from oauth2client.tools import run - - OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.modify' - STORAGE = Storage('oauth.storage') - - # Start the OAuth flow to retrieve credentials - flow = flow_from_clientsecrets(config.client_secret_file, - scope=OAUTH_SCOPE) - http = httplib2.Http() - - # Try to retrieve credentials from storage or run the flow to generate them - credentials = STORAGE.get() - if credentials is None or credentials.invalid: - credentials = run(flow, STORAGE, http=http) - - http = credentials.authorize(http) - return build('gmail', 'v1', http=http) - - -def get_all_messages(service, label): - msgs = [] - response = service.users().messages().list( - userId='me', labelIds=label).execute() - if 'messages' in response: - msgs.extend(response['messages']) - while 'nextPageToken' in response: - page_token = response['nextPageToken'] - response = service.users().messages().list( - userId='me', pageToken=page_token).execute() - msgs.extend(response['messages']) - return msgs - - -def get_body(msg): - if 'attachmentId' in msg['payload']['body']: - raise NotImplementedError('Handling of messages contained in ' - 'attachments not yet implemented.') - b64_body = msg['payload']['body']['data'] - return base64.urlsafe_b64decode(b64_body.encode('ASCII')) - - -def get_gerrit_info(body): - info = {} - gerrit_pattern = r'^Gerrit-(\S+): (.+)$' - for match in re.finditer(gerrit_pattern, body, flags=re.MULTILINE): - info[match.group(1)] = match.group(2).strip() - return info - - def clean_project(dry_run): username = config.jenkins_credentials['username'] password = config.jenkins_credentials['password'] @@ -215,8 +138,6 @@ def handle_change(gerrit_info, _, dry_run): if should_skip_build(gerrit_info): return True return build_project(gerrit_info, dry_run) -handle_newchange = handle_change -handle_newpatchset = handle_change def drop_rejection(gerrit_info, dry_run): @@ -280,75 +201,3 @@ def skip_handler(gerrit_info, _, __): logging.info('Skipping %s: %s', gerrit_info['MessageType'], gerrit_info['Change-Id']) return True - - -handle_abandon = skip_handler -handle_merge_failed = skip_handler -handle_merged = skip_handler -handle_restore = skip_handler -handle_revert = skip_handler - - -def process_message(msg, dry_run): - try: - body = get_body(msg) - gerrit_info = get_gerrit_info(body) - if not gerrit_info: - logging.fatal('No Gerrit info found: %s', msg.subject) - msg_type = gerrit_info['MessageType'] - handler = 'handle_{}'.format( - gerrit_info['MessageType'].replace('-', '_')) - if handler in globals(): - return globals()[handler](gerrit_info, body, dry_run) - else: - logging.warning('MessageType %s unhandled.', msg_type) - return False - except NotImplementedError as ex: - logging.error("%s", ex) - return False - except gerrit.GerritError as ex: - change_id = gerrit_info['Change-Id'] - logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url) - return ex.code == 404 - - -def main(argc, argv): - dry_run = False - if argc == 2 and argv[1] == '--dry-run': - dry_run = True - elif argc > 2: - sys.exit('usage: python {} [--dry-run]'.format(argv[0])) - - gmail_service = build_service() - msg_service = gmail_service.users().messages() - - while True: - try: - labels = gmail_service.users().labels().list(userId='me').execute() - if not labels['labels']: - raise GmailError('Could not retrieve Gmail labels') - label_id = get_gerrit_label(labels['labels']) - if not label_id: - raise GmailError('Could not find gerrit label') - - for msg in get_all_messages(gmail_service, label_id): - msg = msg_service.get(userId='me', id=msg['id']).execute() - if process_message(msg, dry_run) and not dry_run: - msg_service.trash(userId='me', id=msg['id']).execute() - time.sleep(60 * 5) - except GmailError as ex: - logging.error('Gmail error: %s', ex) - time.sleep(60 * 5) - except apiclient.errors.HttpError as ex: - logging.error('API Client HTTP error: %s', ex) - time.sleep(60 * 5) - except httplib.BadStatusLine: - pass - except httplib2.ServerNotFoundError: - pass - except socket.error: - pass - - -if __name__ == '__main__': - main(len(sys.argv), sys.argv) diff --git a/tools/bionicbb/tasks.py b/tools/bionicbb/tasks.py new file mode 100644 index 000000000..4c39a9830 --- /dev/null +++ b/tools/bionicbb/tasks.py @@ -0,0 +1,108 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import httplib +import httplib2 +import logging +import re +import socket + +import apiclient.errors + +import gerrit +import gmail +import presubmit + + +def get_gerrit_info(body): + info = {} + gerrit_pattern = r'^Gerrit-(\S+): (.+)$' + for match in re.finditer(gerrit_pattern, body, flags=re.MULTILINE): + info[match.group(1)] = match.group(2).strip() + return info + + +def process_message(msg, dry_run): + try: + body = gmail.get_body(msg) + gerrit_info = get_gerrit_info(body) + if not gerrit_info: + logging.fatal('No Gerrit info found: %s', msg.subject) + msg_type = gerrit_info['MessageType'] + handlers = { + 'comment': presubmit.handle_comment, + 'newchange': presubmit.handle_change, + 'newpatchset': presubmit.handle_change, + + 'abandon': presubmit.skip_handler, + 'merge-failed': presubmit.skip_handler, + 'merged': presubmit.skip_handler, + 'restore': presubmit.skip_handler, + 'revert': presubmit.skip_handler, + } + + message_type = gerrit_info['MessageType'] + if message_type in handlers: + return handlers[message_type](gerrit_info, body, dry_run) + else: + logging.warning('MessageType %s unhandled.', msg_type) + return False + except NotImplementedError as ex: + logging.error("%s", ex) + return False + except gerrit.GerritError as ex: + change_id = gerrit_info['Change-Id'] + logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url) + return ex.code == 404 + + +def get_and_process_jobs(): + dry_run = False + + gmail_service = gmail.build_service() + msg_service = gmail_service.users().messages() + + # We run in a loop because some of the exceptions thrown here mean we just + # need to retry. For errors where we should back off (typically any gmail + # API exceptions), process_changes catches the error and returns normally. + while True: + try: + process_changes(gmail_service, msg_service, dry_run) + return + except httplib.BadStatusLine: + pass + except httplib2.ServerNotFoundError: + pass + except socket.error: + pass + + +def process_changes(gmail_service, msg_service, dry_run): + try: + labels = gmail_service.users().labels().list(userId='me').execute() + if not labels['labels']: + logging.error('Could not retrieve Gmail labels') + return + label_id = gmail.get_gerrit_label(labels['labels']) + if not label_id: + logging.error('Could not find gerrit label') + return + + for msg in gmail.get_all_messages(gmail_service, label_id): + msg = msg_service.get(userId='me', id=msg['id']).execute() + if process_message(msg, dry_run) and not dry_run: + msg_service.trash(userId='me', id=msg['id']).execute() + except apiclient.errors.HttpError as ex: + logging.error('API Client HTTP error: %s', ex) diff --git a/tools/bionicbb/test_gmail_listener.py b/tools/bionicbb/test_tasks.py similarity index 74% rename from tools/bionicbb/test_gmail_listener.py rename to tools/bionicbb/test_tasks.py index f8b9ab602..b36cbad59 100644 --- a/tools/bionicbb/test_gmail_listener.py +++ b/tools/bionicbb/test_tasks.py @@ -1,11 +1,12 @@ -import gmail_listener import mock import unittest +import presubmit + class TestShouldSkipBuild(unittest.TestCase): - @mock.patch('gmail_listener.contains_bionicbb') - @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('presubmit.contains_bionicbb') + @mock.patch('presubmit.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_accepts_googlers(self, mock_commit, *other_checks): mock_commit.return_value = { @@ -16,14 +17,14 @@ class TestShouldSkipBuild(unittest.TestCase): other_check.return_value = False for message_type in ('newchange', 'newpatchset', 'comment'): - self.assertFalse(gmail_listener.should_skip_build({ + self.assertFalse(presubmit.should_skip_build({ 'MessageType': message_type, 'Change-Id': '', 'PatchSet': '', })) - @mock.patch('gmail_listener.contains_bionicbb') - @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('presubmit.contains_bionicbb') + @mock.patch('presubmit.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_rejects_googlish_domains(self, mock_commit, *other_checks): mock_commit.return_value = { @@ -34,14 +35,14 @@ class TestShouldSkipBuild(unittest.TestCase): other_check.return_value = False for message_type in ('newchange', 'newpatchset', 'comment'): - self.assertTrue(gmail_listener.should_skip_build({ + self.assertTrue(presubmit.should_skip_build({ 'MessageType': message_type, 'Change-Id': '', 'PatchSet': '', })) - @mock.patch('gmail_listener.contains_bionicbb') - @mock.patch('gmail_listener.contains_cleanspec') + @mock.patch('presubmit.contains_bionicbb') + @mock.patch('presubmit.contains_cleanspec') @mock.patch('gerrit.get_commit') def test_rejects_non_googlers(self, mock_commit, *other_checks): mock_commit.return_value = { @@ -52,14 +53,14 @@ class TestShouldSkipBuild(unittest.TestCase): other_check.return_value = False for message_type in ('newchange', 'newpatchset', 'comment'): - self.assertTrue(gmail_listener.should_skip_build({ + self.assertTrue(presubmit.should_skip_build({ 'MessageType': message_type, 'Change-Id': '', 'PatchSet': '', })) - @mock.patch('gmail_listener.contains_bionicbb') - @mock.patch('gmail_listener.is_untrusted_committer') + @mock.patch('presubmit.contains_bionicbb') + @mock.patch('presubmit.is_untrusted_committer') @mock.patch('gerrit.get_files_for_revision') def test_skips_cleanspecs(self, mock_files, *other_checks): mock_files.return_value = ['foo/CleanSpec.mk'] @@ -67,14 +68,14 @@ class TestShouldSkipBuild(unittest.TestCase): other_check.return_value = False for message_type in ('newchange', 'newpatchset', 'comment'): - self.assertTrue(gmail_listener.should_skip_build({ + self.assertTrue(presubmit.should_skip_build({ 'MessageType': message_type, 'Change-Id': '', 'PatchSet': '', })) - @mock.patch('gmail_listener.contains_cleanspec') - @mock.patch('gmail_listener.is_untrusted_committer') + @mock.patch('presubmit.contains_cleanspec') + @mock.patch('presubmit.is_untrusted_committer') @mock.patch('gerrit.get_files_for_revision') def test_skips_bionicbb(self, mock_files, *other_checks): mock_files.return_value = ['tools/bionicbb/common.sh'] @@ -82,7 +83,7 @@ class TestShouldSkipBuild(unittest.TestCase): other_check.return_value = False for message_type in ('newchange', 'newpatchset', 'comment'): - self.assertTrue(gmail_listener.should_skip_build({ + self.assertTrue(presubmit.should_skip_build({ 'MessageType': message_type, 'Change-Id': '', 'PatchSet': '', From 21988a3b1607cc4eb9f185109ed42c509b519662 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 17 Apr 2015 17:51:39 -0700 Subject: [PATCH 091/123] Also send bionicbb logs to a file. Change-Id: If9a6fdbe004e3b4bb7d868b7255f83c232759f80 --- tools/bionicbb/bionicbb.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/bionicbb/bionicbb.py b/tools/bionicbb/bionicbb.py index 20d6460ed..a786b27ed 100644 --- a/tools/bionicbb/bionicbb.py +++ b/tools/bionicbb/bionicbb.py @@ -120,6 +120,10 @@ def drop_rejection(): if __name__ == "__main__": logging.basicConfig(level=logging.INFO) + logger = logging.getLogger() + fh = logging.FileHandler('bionicbb.log') + fh.setLevel(logging.INFO) + logger.addHandler(fh) # Prevent the job from being rescheduled by the reloader. if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': From 4ae773633aac9ceba5ec62bb8e1aa22c3c56ebde Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Fri, 17 Apr 2015 18:16:57 -0400 Subject: [PATCH 092/123] add fortified memchr/memrchr implementations Change-Id: I38c473cc0c608b08f971409a95eb8b853cb2ba1c --- libc/Android.mk | 2 ++ libc/bionic/__memchr_chk.cpp | 39 ++++++++++++++++++++++++++++ libc/bionic/__memrchr_chk.cpp | 39 ++++++++++++++++++++++++++++ libc/include/string.h | 49 +++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 libc/bionic/__memchr_chk.cpp create mode 100644 libc/bionic/__memrchr_chk.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 9d3b62aeb..6256caf12 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -69,7 +69,9 @@ libc_common_src_files := \ libc_common_src_files += \ bionic/__FD_chk.cpp \ bionic/__fgets_chk.cpp \ + bionic/__memchr_chk.cpp \ bionic/__memmove_chk.cpp \ + bionic/__memrchr_chk.cpp \ bionic/__poll_chk.cpp \ bionic/__pread64_chk.cpp \ bionic/__pread_chk.cpp \ diff --git a/libc/bionic/__memchr_chk.cpp b/libc/bionic/__memchr_chk.cpp new file mode 100644 index 000000000..d141c045a --- /dev/null +++ b/libc/bionic/__memchr_chk.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" void* __memchr_chk(const void* s, int c, size_t n, size_t buf_size) { + if (__predict_false(n > buf_size)) { + __fortify_chk_fail("memchr: prevented read past end of buffer", 0); + } + + return memchr(s, c, n); +} diff --git a/libc/bionic/__memrchr_chk.cpp b/libc/bionic/__memrchr_chk.cpp new file mode 100644 index 000000000..8529dfc16 --- /dev/null +++ b/libc/bionic/__memrchr_chk.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 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. + */ + +#undef _FORTIFY_SOURCE +#include +#include "private/libc_logging.h" + +extern "C" void* __memrchr_chk(const void* s, int c, size_t n, size_t buf_size) { + if (__predict_false(n > buf_size)) { + __fortify_chk_fail("memrchr: prevented read past end of buffer", 0); + } + + return memrchr(s, c, n); +} diff --git a/libc/include/string.h b/libc/include/string.h index fffe1362c..d32c16458 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -121,6 +121,13 @@ extern char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1)); #define __bionic_using_gnu_basename #endif +extern void* __memchr_chk(const void*, int, size_t, size_t); +__errordecl(__memchr_buf_size_error, "memchr called with size bigger than buffer"); + +extern void* __memrchr_chk(const void*, int, size_t, size_t); +__errordecl(__memrchr_buf_size_error, "memrchr called with size bigger than buffer"); +extern void* __memrchr_real(const void*, int, size_t) __RENAME(memrchr); + extern char* __stpncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t); extern char* __strncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t); extern size_t __strlcpy_real(char* __restrict, const char* __restrict, size_t) __RENAME(strlcpy); @@ -130,6 +137,48 @@ extern size_t __strlcat_chk(char* __restrict, const char* __restrict, size_t, si #if defined(__BIONIC_FORTIFY) +__BIONIC_FORTIFY_INLINE +void* memchr(const void *s, int c, size_t n) { + size_t bos = __bos(s); + +#if !defined(__clang__) + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __builtin_memchr(s, c, n); + } + + if (__builtin_constant_p(n) && (n > bos)) { + __memchr_buf_size_error(); + } + + if (__builtin_constant_p(n) && (n <= bos)) { + return __builtin_memchr(s, c, n); + } +#endif + + return __memchr_chk(s, c, n, bos); +} + +__BIONIC_FORTIFY_INLINE +void* memrchr(const void *s, int c, size_t n) { + size_t bos = __bos(s); + +#if !defined(__clang__) + if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) { + return __memrchr_real(s, c, n); + } + + if (__builtin_constant_p(n) && (n > bos)) { + __memrchr_buf_size_error(); + } + + if (__builtin_constant_p(n) && (n <= bos)) { + return __memrchr_real(s, c, n); + } +#endif + + return __memrchr_chk(s, c, n, bos); +} + __BIONIC_FORTIFY_INLINE void* memcpy(void* __restrict dest, const void* __restrict src, size_t copy_amount) { return __builtin___memcpy_chk(dest, src, copy_amount, __bos0(dest)); From 72a6fdcdc71df623f8876cf0681dc9364e78b35a Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Sat, 18 Apr 2015 14:07:41 -0700 Subject: [PATCH 093/123] Fix bug in app_id_from_name in stubs.cpp. It seems that a break statement is missing. Bug: 19872411 Change-Id: I9362783ab726d01f6eb27418563e716dd95688dc --- libc/bionic/stubs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index c971d1b59..b57aedab4 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -245,6 +245,7 @@ static id_t app_id_from_name(const char* name, bool is_group) { appid = android_ids[n].aid; // Move the end pointer to the null terminator. end += strlen(android_ids[n].name) + 1; + break; } } } From 3244d9f07fda946d62afdfa61ed5a876d380d0ff Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sat, 18 Apr 2015 13:04:19 -0400 Subject: [PATCH 094/123] add a fortified implementation of realpath Change-Id: Icc59eacd1684f7cddd83d7a2b57dad0c7ada5eb7 --- libc/include/stdlib.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index 84bf56dee..efca5774a 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -176,6 +176,27 @@ extern size_t __ctype_get_mb_cur_max(void); #include #endif +#if defined(__BIONIC_FORTIFY) + +extern char* __realpath_real(const char*, char*) __RENAME(realpath); +__errordecl(__realpath_size_error, "realpath output parameter must be NULL or a >= PATH_MAX bytes buffer"); + +#if !defined(__clang__) +__BIONIC_FORTIFY_INLINE +char* realpath(const char* path, char* resolved) { + size_t bos = __bos(resolved); + + /* PATH_MAX is unavailable without polluting the namespace, but it's always 4096 on Linux */ + if (bos != __BIONIC_FORTIFY_UNKNOWN_SIZE && bos < 4096) { + __realpath_size_error(); + } + + return __realpath_real(path, resolved); +} +#endif + +#endif /* defined(__BIONIC_FORTIFY) */ + __END_DECLS #endif /* _STDLIB_H */ From 3d7bea1fa00342f2a18331ea33a4b6e3332b3b02 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 20 Apr 2015 17:40:39 -0700 Subject: [PATCH 095/123] Add library name to error message Change-Id: I079e6f1dd95fe9cae2135fcd7358c51f8b584ac9 --- linker/linker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index e029dbdbf..ebc0947c5 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -442,7 +442,8 @@ static bool for_each_verdef(const soinfo* si, F functor) { offset += verdef->vd_next; if (verdef->vd_version != 1) { - DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1)", i, verdef->vd_version); + DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s", + i, verdef->vd_version, si->get_soname()); return false; } From bb25bbe19e4e56aa612aeaaf2c53d79dadebc03e Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 20 Apr 2015 17:41:28 -0700 Subject: [PATCH 096/123] Adjust DT_VERSYM/VERNEED/VERDEF dynamic sections This is recent addition to bionic linker. The symbol versioning was not supported before therefore this bug went unnoticed. Also normal exit when there is not enought relocations to pack. This is to enable integration of relocation_packer to android build system. Bug: http://b/20139821 Bug: http://b/18051137 Change-Id: Iaf36ae11c8e4b15cf785b6dd1712a3bdcf47cc45 --- tools/relocation_packer/src/elf_file.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index 20b25ef82..6843f5bae 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -439,6 +439,9 @@ void ElfFile::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, tag == DT_JMPREL || tag == DT_INIT_ARRAY || tag == DT_FINI_ARRAY || + tag == DT_VERSYM || + tag == DT_VERNEED || + tag == DT_VERDEF || tag == DT_ANDROID_REL|| tag == DT_ANDROID_RELA); @@ -586,7 +589,7 @@ bool ElfFile::PackRelocations() { const typename ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); ConvertRelArrayToRelaVector(relocations_base, data->d_size / sizeof(typename ELF::Rel), &relocations); - LOG(INFO) << "Relocations : REL"; + VLOG(1) << "Relocations : REL"; } else if (relocations_type_ == RELA) { // Convert data to a vector of relocations with addends. const typename ELF::Rela* relocations_base = reinterpret_cast(data->d_buf); @@ -594,7 +597,7 @@ bool ElfFile::PackRelocations() { relocations_base, relocations_base + data->d_size / sizeof(relocations[0])); - LOG(INFO) << "Relocations : RELA"; + VLOG(1) << "Relocations : RELA"; } else { NOTREACHED(); } @@ -618,18 +621,18 @@ bool ElfFile::PackTypedRelocations(std::vector* relocat relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel); const size_t initial_bytes = relocations->size() * rel_size; - LOG(INFO) << "Unpacked : " << initial_bytes << " bytes"; + VLOG(1) << "Unpacked : " << initial_bytes << " bytes"; std::vector packed; RelocationPacker packer; // Pack relocations: dry run to estimate memory savings. packer.PackRelocations(*relocations, &packed); const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]); - LOG(INFO) << "Packed (no padding): " << packed_bytes_estimate << " bytes"; + VLOG(1) << "Packed (no padding): " << packed_bytes_estimate << " bytes"; if (packed.empty()) { LOG(INFO) << "Too few relocations to pack"; - return false; + return true; } // Pre-calculate the size of the hole we will close up when we rewrite @@ -646,7 +649,7 @@ bool ElfFile::PackTypedRelocations(std::vector* relocat // Adjusting for alignment may have removed any packing benefit. if (hole_size == 0) { LOG(INFO) << "Too few relocations to pack after alignment"; - return false; + return true; } if (hole_size <= 0) { From 6fb8e96e5f6ff2dd3b3aa8eda0a6766d6b3a63f2 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Mon, 16 Mar 2015 13:49:40 -0700 Subject: [PATCH 097/123] Allow building libc long double code with clang/llvm. This requires fix of clang's Android x86 long double size and format. That bug has been fixed in https://android-review.git.corp.google.com/#/c/146254/ Change-Id: I182c6c493085212f88c694356659f72227c8b8c7 --- libc/Android.mk | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/libc/Android.mk b/libc/Android.mk index 7bbdd9901..8dbdc7599 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -817,12 +817,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(libc_upstream_openbsd_ndk_src_files) -ifneq (,$(filter $(TARGET_ARCH),x86 x86_64)) - # Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651. - LOCAL_CLANG := false -else - LOCAL_CLANG := $(use_clang) -endif +LOCAL_CLANG := $(use_clang) LOCAL_CFLAGS := \ $(libc_common_cflags) \ @@ -860,12 +855,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(libc_upstream_openbsd_src_files) -ifneq (,$(filter $(TARGET_ARCH),x86 x86_64)) - # Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651. - LOCAL_CLANG := false -else - LOCAL_CLANG := $(use_clang) -endif +LOCAL_CLANG := $(use_clang) LOCAL_CFLAGS := \ $(libc_common_cflags) \ @@ -905,12 +895,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES_32 := $(libc_upstream_openbsd_gdtoa_src_files_32) LOCAL_SRC_FILES_64 := $(libc_upstream_openbsd_gdtoa_src_files_64) -ifneq (,$(filter $(TARGET_ARCH),x86 x86_64)) - # Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651. - LOCAL_CLANG := false -else - LOCAL_CLANG := $(use_clang) -endif +LOCAL_CLANG := $(use_clang) LOCAL_CFLAGS := \ $(libc_common_cflags) \ From 2a6342187a4cbb0a8804394649893b8a306ceaeb Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 22 Apr 2015 10:43:12 -0700 Subject: [PATCH 098/123] Statically link libc++ for prebuilts Statically link libc++ to relocation_packer in order to make it work from prebuilts Bug: http://b/18051137 Change-Id: I933ed6a0e48780a26b261069eb6a293432824fe7 --- tools/relocation_packer/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk index 99a39c02b..eabe0daa3 100644 --- a/tools/relocation_packer/Android.mk +++ b/tools/relocation_packer/Android.mk @@ -46,6 +46,9 @@ LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES := src/main.cc LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf + +# Statically linking libc++ to make it work from prebuilts +LOCAL_CXX_STL := libc++_static LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include LOCAL_MODULE := relocation_packer From 7592008030a67ebe0dbda20aa041d5c347170611 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 22 Apr 2015 10:37:38 -0700 Subject: [PATCH 099/123] Update the search path for libclang.so Prebuilt shared libraries (libclang.so, libLLVM.so and etc) have been moved to prebuilts/sdk/tools/linux/lib64. Update the search path in cpp.py to match the change. Bug: 20485471 Change-Id: Ib7784db4d5529d16a1e2bfc07cb0237929bc5a64 --- libc/kernel/tools/cpp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libc/kernel/tools/cpp.py b/libc/kernel/tools/cpp.py index ff5136e65..10ce29022 100644 --- a/libc/kernel/tools/cpp.py +++ b/libc/kernel/tools/cpp.py @@ -14,7 +14,6 @@ if top is None: # Set up the env vars for libclang. site.addsitedir(os.path.join(top, 'external/clang/bindings/python')) -os.putenv('LD_LIBRARY_PATH', os.path.join(top, 'prebuilts/sdk/tools/linux')) import clang.cindex from clang.cindex import conf @@ -26,6 +25,10 @@ from clang.cindex import TokenGroup from clang.cindex import TokenKind from clang.cindex import TranslationUnit +# Set up LD_LIBRARY_PATH to include libclang.so, libLLVM.so, and etc. +# Note that setting LD_LIBRARY_PATH with os.putenv() sometimes doesn't help. +clang.cindex.Config.set_library_path(os.path.join(top, 'prebuilts/sdk/tools/linux/lib64')) + from defaults import kCppUndefinedMacro from defaults import kernel_remove_config_macros from defaults import kernel_token_replacements From f15ceeb7841ef97c24a0b7708732756d433c5d0d Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 21 Apr 2015 15:03:04 -0700 Subject: [PATCH 100/123] Always use signed leb128 encoding According to runs on /system/lib there using unsigned leb128 does not save us any additional space. In order to keep packing as simple as possible switch to using signed leb128 for everything. Bug: http://b/18051137 Change-Id: I1a47cb9eb2175895b3c3f7c13b4c6b1060de86c0 --- tools/relocation_packer/Android.mk | 2 - tools/relocation_packer/src/elf_file.cc | 16 ++- tools/relocation_packer/src/elf_file.h | 5 +- .../src/elf_file_unittest.cc | 10 +- tools/relocation_packer/src/leb128.cc | 87 -------------- tools/relocation_packer/src/leb128.h | 75 ------------ .../relocation_packer/src/leb128_unittest.cc | 111 ------------------ tools/relocation_packer/src/packer.cc | 37 ++---- .../relocation_packer/src/packer_unittest.cc | 24 ++-- .../elf_file_unittest_relocs_arm32_packed.so | Bin 89114 -> 89114 bytes .../elf_file_unittest_relocs_arm64_packed.so | Bin 113651 -> 113651 bytes 11 files changed, 50 insertions(+), 317 deletions(-) delete mode 100644 tools/relocation_packer/src/leb128.cc delete mode 100644 tools/relocation_packer/src/leb128.h delete mode 100644 tools/relocation_packer/src/leb128_unittest.cc diff --git a/tools/relocation_packer/Android.mk b/tools/relocation_packer/Android.mk index eabe0daa3..75dba7143 100644 --- a/tools/relocation_packer/Android.mk +++ b/tools/relocation_packer/Android.mk @@ -26,7 +26,6 @@ LOCAL_SRC_FILES := \ src/debug.cc \ src/delta_encoder.cc \ src/elf_file.cc \ - src/leb128.cc \ src/packer.cc \ src/sleb128.cc \ @@ -67,7 +66,6 @@ LOCAL_SRC_FILES := \ src/debug_unittest.cc \ src/delta_encoder_unittest.cc \ src/elf_file_unittest.cc \ - src/leb128_unittest.cc \ src/sleb128_unittest.cc \ src/packer_unittest.cc \ diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index 6843f5bae..11af9ed97 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -190,6 +190,7 @@ bool ElfFile::Load() { // these; both is unsupported. bool has_rel_relocations = false; bool has_rela_relocations = false; + bool has_android_relocations = false; Elf_Scn* section = NULL; while ((section = elf_nextscn(elf, section)) != nullptr) { @@ -209,6 +210,11 @@ bool ElfFile::Load() { if ((name == ".rel.dyn" || name == ".rela.dyn") && section_header->sh_size > 0) { found_relocations_section = section; + + // Note if relocation section is already packed + has_android_relocations = + section_header->sh_type == SHT_ANDROID_REL || + section_header->sh_type == SHT_ANDROID_RELA; } if (section_header->sh_offset == dynamic_program_header->p_offset) { @@ -250,6 +256,7 @@ bool ElfFile::Load() { relocations_section_ = found_relocations_section; dynamic_section_ = found_dynamic_section; relocations_type_ = has_rel_relocations ? REL : RELA; + has_android_relocations_ = has_android_relocations; return true; } @@ -610,10 +617,15 @@ template bool ElfFile::PackTypedRelocations(std::vector* relocations) { typedef typename ELF::Rela Rela; + if (has_android_relocations_) { + LOG(ERROR) << "Relocation table is already packed"; + return false; + } + // If no relocations then we have nothing packable. Perhaps // the shared object has already been packed? if (relocations->empty()) { - LOG(ERROR) << "No relocations found (already packed?)"; + LOG(ERROR) << "No relocations found"; return false; } @@ -737,7 +749,7 @@ bool ElfFile::UnpackRelocations() { packed.size() > 3 && packed[0] == 'A' && packed[1] == 'P' && - (packed[2] == 'U' || packed[2] == 'S') && + packed[2] == 'S' && packed[3] == '2') { LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA"); } else { diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h index a749d5066..d6acc76b7 100644 --- a/tools/relocation_packer/src/elf_file.h +++ b/tools/relocation_packer/src/elf_file.h @@ -36,7 +36,7 @@ class ElfFile { explicit ElfFile(int fd) : fd_(fd), is_padding_relocations_(false), elf_(NULL), relocations_section_(NULL), dynamic_section_(NULL), - relocations_type_(NONE) {} + relocations_type_(NONE), has_android_relocations_(false) {} ~ElfFile() {} // Set padding mode. When padding, PackRelocations() will not shrink @@ -111,6 +111,9 @@ class ElfFile { // Relocation type found, assigned by Load(). relocations_type_t relocations_type_; + + // Elf-file has android relocations section + bool has_android_relocations_; }; } // namespace relocation_packer diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc index 434f10102..5271eef87 100644 --- a/tools/relocation_packer/src/elf_file_unittest.cc +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -175,13 +175,19 @@ static void RunPackRelocationsTestFor(const std::string& arch) { namespace relocation_packer { -TEST(ElfFile, PackRelocations) { +TEST(ElfFile, PackRelocationsArm32) { RunPackRelocationsTestFor("arm32"); +} + +TEST(ElfFile, PackRelocationsArm64) { RunPackRelocationsTestFor("arm64"); } -TEST(ElfFile, UnpackRelocations) { +TEST(ElfFile, UnpackRelocationsArm32) { RunUnpackRelocationsTestFor("arm32"); +} + +TEST(ElfFile, UnpackRelocationsArm64) { RunUnpackRelocationsTestFor("arm64"); } diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc deleted file mode 100644 index 101c55778..000000000 --- a/tools/relocation_packer/src/leb128.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "leb128.h" - -#include -#include - -#include "elf_traits.h" - -namespace relocation_packer { - -// Empty constructor and destructor to silence chromium-style. -template -Leb128Encoder::Leb128Encoder() { } - -template -Leb128Encoder::~Leb128Encoder() { } - -// Add a single value to the encoding. Values are encoded with variable -// length. The least significant 7 bits of each byte hold 7 bits of data, -// and the most significant bit is set on each byte except the last. -template -void Leb128Encoder::Enqueue(uint_t value) { - uint_t uvalue = static_cast(value); - do { - const uint8_t byte = uvalue & 127; - uvalue >>= 7; - encoding_.push_back((uvalue ? 128 : 0) | byte); - } while (uvalue); -} - -// Add a vector of values to the encoding. -template -void Leb128Encoder::EnqueueAll(const std::vector& values) { - for (size_t i = 0; i < values.size(); ++i) { - Enqueue(values[i]); - } -} - -// Create a new decoder for the given encoded stream. -template -Leb128Decoder::Leb128Decoder(const std::vector& encoding, size_t start_with) { - encoding_ = encoding; - cursor_ = start_with; -} - -// Empty destructor to silence chromium-style. -template -Leb128Decoder::~Leb128Decoder() { } - -// Decode and retrieve a single value from the encoding. Read forwards until -// a byte without its most significant bit is found, then read the 7 bit -// fields of the bytes spanned to re-form the value. -template -uint_t Leb128Decoder::Dequeue() { - uint_t value = 0; - - size_t shift = 0; - uint8_t byte; - - // Loop until we reach a byte with its high order bit clear. - do { - byte = encoding_[cursor_++]; - value |= static_cast(byte & 127) << shift; - shift += 7; - } while (byte & 128); - - return value; -} - -// Decode and retrieve all remaining values from the encoding. -template -void Leb128Decoder::DequeueAll(std::vector* values) { - while (cursor_ < encoding_.size()) { - values->push_back(Dequeue()); - } -} - -template class Leb128Encoder; -template class Leb128Encoder; - -template class Leb128Decoder; -template class Leb128Decoder; - -} // namespace relocation_packer diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h deleted file mode 100644 index 67fc4b829..000000000 --- a/tools/relocation_packer/src/leb128.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// LEB128 encoder and decoder for packed relative relocations. -// -// Packed relocations consist of a large number of relatively small -// integer values. Encoding these as LEB128 saves space. -// -// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. - -#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ -#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ - -#include -#include - -#include "elf_traits.h" - -namespace relocation_packer { - -// Encode packed words as a LEB128 byte stream. -template -class Leb128Encoder { - public: - // Explicit (but empty) constructor and destructor, for chromium-style. - Leb128Encoder(); - ~Leb128Encoder(); - - // Add a value to the encoding stream. - // |value| is the unsigned int to add. - void Enqueue(uint_t value); - - // Add a vector of values to the encoding stream. - // |values| is the vector of unsigned ints to add. - void EnqueueAll(const std::vector& values); - - // Retrieve the encoded representation of the values. - // |encoding| is the returned vector of encoded data. - void GetEncoding(std::vector* encoding) { *encoding = encoding_; } - - private: - // Growable vector holding the encoded LEB128 stream. - std::vector encoding_; -}; - -// Decode a LEB128 byte stream to produce packed words. -template -class Leb128Decoder { - public: - // Create a new decoder for the given encoded stream. - // |encoding| is the vector of encoded data. - explicit Leb128Decoder(const std::vector& encoding, size_t start_with); - - // Explicit (but empty) destructor, for chromium-style. - ~Leb128Decoder(); - - // Retrieve the next value from the encoded stream. - uint_t Dequeue(); - - // Retrieve all remaining values from the encoded stream. - // |values| is the vector of decoded data. - void DequeueAll(std::vector* values); - - private: - // Encoded LEB128 stream. - std::vector encoding_; - - // Cursor indicating the current stream retrieval point. - size_t cursor_; -}; - -} // namespace relocation_packer - -#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc deleted file mode 100644 index 8a7028cbc..000000000 --- a/tools/relocation_packer/src/leb128_unittest.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "leb128.h" - -#include -#include "gtest/gtest.h" - -namespace relocation_packer { - -TEST(Leb128, Encoder64) { - std::vector values; - values.push_back(624485); - values.push_back(0); - values.push_back(1); - values.push_back(127); - values.push_back(128); - - Leb128Encoder encoder; - encoder.EnqueueAll(values); - - encoder.Enqueue(4294967295); - encoder.Enqueue(18446744073709551615ul); - - std::vector encoding; - encoder.GetEncoding(&encoding); - - EXPECT_EQ(23U, encoding.size()); - // 624485 - EXPECT_EQ(0xe5, encoding[0]); - EXPECT_EQ(0x8e, encoding[1]); - EXPECT_EQ(0x26, encoding[2]); - // 0 - EXPECT_EQ(0x00, encoding[3]); - // 1 - EXPECT_EQ(0x01, encoding[4]); - // 127 - EXPECT_EQ(0x7f, encoding[5]); - // 128 - EXPECT_EQ(0x80, encoding[6]); - EXPECT_EQ(0x01, encoding[7]); - // 4294967295 - EXPECT_EQ(0xff, encoding[8]); - EXPECT_EQ(0xff, encoding[9]); - EXPECT_EQ(0xff, encoding[10]); - EXPECT_EQ(0xff, encoding[11]); - EXPECT_EQ(0x0f, encoding[12]); - // 18446744073709551615 - EXPECT_EQ(0xff, encoding[13]); - EXPECT_EQ(0xff, encoding[14]); - EXPECT_EQ(0xff, encoding[15]); - EXPECT_EQ(0xff, encoding[16]); - EXPECT_EQ(0xff, encoding[17]); - EXPECT_EQ(0xff, encoding[18]); - EXPECT_EQ(0xff, encoding[19]); - EXPECT_EQ(0xff, encoding[20]); - EXPECT_EQ(0xff, encoding[21]); - EXPECT_EQ(0x01, encoding[22]); -} - -TEST(Leb128, Decoder64) { - std::vector encoding; - // 624485 - encoding.push_back(0xe5); - encoding.push_back(0x8e); - encoding.push_back(0x26); - // 0 - encoding.push_back(0x00); - // 1 - encoding.push_back(0x01); - // 127 - encoding.push_back(0x7f); - // 128 - encoding.push_back(0x80); - encoding.push_back(0x01); - // 4294967295 - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0x0f); - // 18446744073709551615 - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0xff); - encoding.push_back(0x01); - - Leb128Decoder decoder(encoding, 0); - - EXPECT_EQ(624485U, decoder.Dequeue()); - - std::vector dequeued; - decoder.DequeueAll(&dequeued); - - EXPECT_EQ(6U, dequeued.size()); - EXPECT_EQ(0U, dequeued[0]); - EXPECT_EQ(1U, dequeued[1]); - EXPECT_EQ(127U, dequeued[2]); - EXPECT_EQ(128U, dequeued[3]); - EXPECT_EQ(4294967295U, dequeued[4]); - EXPECT_EQ(18446744073709551615UL, dequeued[5]); -} - -} // namespace relocation_packer diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc index 8e30612e5..433611faa 100644 --- a/tools/relocation_packer/src/packer.cc +++ b/tools/relocation_packer/src/packer.cc @@ -9,7 +9,6 @@ #include "debug.h" #include "delta_encoder.h" #include "elf_traits.h" -#include "leb128.h" #include "sleb128.h" namespace relocation_packer { @@ -28,32 +27,17 @@ void RelocationPacker::PackRelocations(const std::vector sleb128_encoder; - Leb128Encoder leb128_encoder; - std::vector leb128_packed; std::vector sleb128_packed; - leb128_encoder.EnqueueAll(packed_words); - leb128_encoder.GetEncoding(&leb128_packed); - sleb128_encoder.EnqueueAll(packed_words); sleb128_encoder.GetEncoding(&sleb128_packed); - // TODO (simonb): Estimate savings on current android system image and consider using - // one encoder for all packed relocations to reduce complexity. - if (leb128_packed.size() <= sleb128_packed.size()) { - packed->push_back('A'); - packed->push_back('P'); - packed->push_back('U'); - packed->push_back('2'); - packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end()); - } else { - packed->push_back('A'); - packed->push_back('P'); - packed->push_back('S'); - packed->push_back('2'); - packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end()); - } + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('S'); + packed->push_back('2'); + packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end()); } // Unpack relative relocations from a run-length encoded packed @@ -67,16 +51,11 @@ void RelocationPacker::UnpackRelocations( CHECK(packed.size() > 4 && packed[0] == 'A' && packed[1] == 'P' && - (packed[2] == 'U' || packed[2] == 'S') && + packed[2] == 'S' && packed[3] == '2'); - if (packed[2] == 'U') { - Leb128Decoder decoder(packed, 4); - decoder.DequeueAll(&packed_words); - } else { - Sleb128Decoder decoder(packed, 4); - decoder.DequeueAll(&packed_words); - } + Sleb128Decoder decoder(packed, 4); + decoder.DequeueAll(&packed_words); RelocationDeltaCodec codec; codec.Decode(packed_words, relocations); diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc index 8dddd8b41..424b92cd0 100644 --- a/tools/relocation_packer/src/packer_unittest.cc +++ b/tools/relocation_packer/src/packer_unittest.cc @@ -39,6 +39,7 @@ template static void DoPackNoAddend() { std::vector relocations; std::vector packed; + bool is_32 = sizeof(typename ELF::Addr) == 4; // Initial relocation. AddRelocation(0xd1ce0000, 0x11, 0, &relocations); // Two more relocations, 4 byte deltas. @@ -59,16 +60,16 @@ static void DoPackNoAddend() { size_t ndx = 0; EXPECT_EQ('A', packed[ndx++]); EXPECT_EQ('P', packed[ndx++]); - EXPECT_EQ('U', packed[ndx++]); + EXPECT_EQ('S', packed[ndx++]); EXPECT_EQ('2', packed[ndx++]); // relocation count EXPECT_EQ(6, packed[ndx++]); - // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 7d/0d (32/64bit) EXPECT_EQ(0xfc, packed[ndx++]); EXPECT_EQ(0xff, packed[ndx++]); EXPECT_EQ(0xb7, packed[ndx++]); EXPECT_EQ(0x8e, packed[ndx++]); - EXPECT_EQ(0x0d, packed[ndx++]); + EXPECT_EQ(is_32 ? 0x7d : 0x0d, packed[ndx++]); // first group EXPECT_EQ(3, packed[ndx++]); // size EXPECT_EQ(3, packed[ndx++]); // flags @@ -83,8 +84,11 @@ static void DoPackNoAddend() { EXPECT_EQ(ndx, packed.size()); } -TEST(Packer, PackNoAddend) { +TEST(Packer, PackNoAddend32) { DoPackNoAddend(); +} + +TEST(Packer, PackNoAddend64) { DoPackNoAddend(); } @@ -92,18 +96,19 @@ template static void DoUnpackNoAddend() { std::vector relocations; std::vector packed; + bool is_32 = sizeof(typename ELF::Addr) == 4; packed.push_back('A'); packed.push_back('P'); - packed.push_back('U'); + packed.push_back('S'); packed.push_back('2'); // relocation count packed.push_back(6); - // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d + // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 7d/0d (32/64bit) packed.push_back(0xfc); packed.push_back(0xff); packed.push_back(0xb7); packed.push_back(0x8e); - packed.push_back(0x0d); + packed.push_back(is_32 ? 0x7d : 0x0d); // first group packed.push_back(3); // size packed.push_back(3); // flags @@ -131,8 +136,11 @@ static void DoUnpackNoAddend() { EXPECT_EQ(ndx, relocations.size()); } -TEST(Packer, UnpackNoAddend) { +TEST(Packer, UnpackNoAddend32) { DoUnpackNoAddend(); +} + +TEST(Packer, UnpackNoAddend64) { DoUnpackNoAddend(); } diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so index d97ef825260d3a5dfec58d42b477693dc6220532..6ac2eef059748100a76160e041941f4790f3933b 100755 GIT binary patch delta 31 ncmbQWgLT#p)(v7z?7>Da+5h}z*et_j&)*y)xIIRYai13ey-Euv delta 30 mcmbQWgLT#p)(v7zY@tRk+5c2;mSwW%Z;lq+9xceY-wOb)^$Mo| diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so index e44e4597b30e56d749a748ae7df6c8756e4cd1db..a2b0039187a95808876f93ef8ccceee0dcd1979c 100755 GIT binary patch delta 20 ccmezTo$d2?whdLxjKR&-%-gG(87p=G0BP+A$p8QV delta 20 ccmezTo$d2?whdLxjG@id%-gG(87p=G0BQjU%K!iX From 18870d350c29c83bdcecbe5cf3715b2c800275f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 22 Apr 2015 13:10:04 -0700 Subject: [PATCH 101/123] Always use signed leb128 decoder Relocation packer no longer encodes relocation tables using unsigned leb128: https://android-review.googlesource.com/147745 Bug: http://b/18051137 Change-Id: I620b7188e5f3dd9d5123431aa1fc7feca76be607 --- linker/linker.cpp | 19 ++++------ linker/linker_mips.cpp | 7 +--- linker/{linker_leb128.h => linker_sleb128.h} | 37 +++----------------- 3 files changed, 11 insertions(+), 52 deletions(-) rename linker/{linker_leb128.h => linker_sleb128.h} (67%) diff --git a/linker/linker.cpp b/linker/linker.cpp index ebc0947c5..be7b10cc1 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -55,7 +55,7 @@ #include "linker_block_allocator.h" #include "linker_debug.h" #include "linker_environ.h" -#include "linker_leb128.h" +#include "linker_sleb128.h" #include "linker_phdr.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" @@ -2875,7 +2875,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& if (android_relocs_size_ > 3 && android_relocs_[0] == 'A' && android_relocs_[1] == 'P' && - (android_relocs_[2] == 'U' || android_relocs_[2] == 'S') && + android_relocs_[2] == 'S' && android_relocs_[3] == '2') { DEBUG("[ android relocating %s ]", get_soname()); @@ -2883,17 +2883,10 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& const uint8_t* packed_relocs = android_relocs_ + 4; const size_t packed_relocs_size = android_relocs_size_ - 4; - if (android_relocs_[2] == 'U') { - relocated = relocate( - packed_reloc_iterator( - leb128_decoder(packed_relocs, packed_relocs_size)), - global_group, local_group); - } else { // android_relocs_[2] == 'S' - relocated = relocate( - packed_reloc_iterator( - sleb128_decoder(packed_relocs, packed_relocs_size)), - global_group, local_group); - } + relocated = relocate( + packed_reloc_iterator( + sleb128_decoder(packed_relocs, packed_relocs_size)), + global_group, local_group); if (!relocated) { return false; diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp index c162111e9..0769f826e 100644 --- a/linker/linker_mips.cpp +++ b/linker/linker_mips.cpp @@ -30,7 +30,7 @@ #include "linker_debug.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" -#include "linker_leb128.h" +#include "linker_sleb128.h" template bool soinfo::relocate(plain_reloc_iterator&& rel_iterator, const soinfo_list_t& global_group, @@ -41,11 +41,6 @@ template bool soinfo::relocate>( const soinfo_list_t& global_group, const soinfo_list_t& local_group); -template bool soinfo::relocate>( - packed_reloc_iterator&& rel_iterator, - const soinfo_list_t& global_group, - const soinfo_list_t& local_group); - template bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, diff --git a/linker/linker_leb128.h b/linker/linker_sleb128.h similarity index 67% rename from linker/linker_leb128.h rename to linker/linker_sleb128.h index d5c6488c0..a34916f87 100644 --- a/linker/linker_leb128.h +++ b/linker/linker_sleb128.h @@ -14,42 +14,14 @@ * limitations under the License. */ -#ifndef _LINKER_LEB128_H -#define _LINKER_LEB128_H +#ifndef _LINKER_SLEB128_H +#define _LINKER_SLEB128_H #include // Helper classes for decoding LEB128, used in packed relocation data. // http://en.wikipedia.org/wiki/LEB128 -class leb128_decoder { - public: - leb128_decoder(const uint8_t* buffer, size_t count) - : current_(buffer), end_(buffer + count) { } - - size_t pop_front() { - size_t value = 0; - - size_t shift = 0; - uint8_t byte; - - do { - if (current_ >= end_) { - __libc_fatal("leb128_decoder ran out of bounds"); - } - byte = *current_++; - value |= static_cast(byte & 127) << shift; - shift += 7; - } while (byte & 128); - - return value; - } - - private: - const uint8_t* current_; - const uint8_t* const end_; -}; - class sleb128_decoder { public: sleb128_decoder(const uint8_t* buffer, size_t count) @@ -64,7 +36,7 @@ class sleb128_decoder { do { if (current_ >= end_) { - __libc_fatal("leb128_decoder ran out of bounds"); + __libc_fatal("sleb128_decoder ran out of bounds"); } byte = *current_++; value |= (static_cast(byte & 127) << shift); @@ -83,5 +55,4 @@ class sleb128_decoder { const uint8_t* const end_; }; -#endif // __LINKER_LEB128_H - +#endif // __LINKER_SLEB128_H From 6df122f8528f9b9fcf7dfea14ae98b0ef66274e1 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 20 Nov 2014 20:47:02 -0800 Subject: [PATCH 102/123] Unregister pthread_atfork handlers on dlclose() Change-Id: I326fdf6bb06bed12743f08980b5c69d849c015b8 --- libc/Android.mk | 2 +- libc/arch-arm64/bionic/crtbegin.c | 1 + libc/arch-common/bionic/crtbegin.c | 1 + libc/arch-common/bionic/crtbegin_so.c | 1 + libc/arch-common/bionic/pthread_atfork.h | 29 +++++ libc/arch-mips/bionic/crtbegin.c | 1 + libc/bionic/pthread_atfork.cpp | 122 ++++++++++++++---- .../lib/libc => }/stdlib/atexit.c | 16 ++- .../lib/libc => }/stdlib/atexit.h | 0 tests/libs/Android.build.pthread_atfork.mk | 25 ++++ tests/libs/Android.mk | 6 + tests/libs/pthread_atfork.cpp | 21 +++ tests/pthread_test.cpp | 77 +++++++++-- 13 files changed, 261 insertions(+), 41 deletions(-) create mode 100644 libc/arch-common/bionic/pthread_atfork.h rename libc/{upstream-openbsd/lib/libc => }/stdlib/atexit.c (92%) rename libc/{upstream-openbsd/lib/libc => }/stdlib/atexit.h (100%) create mode 100644 tests/libs/Android.build.pthread_atfork.mk create mode 100644 tests/libs/pthread_atfork.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 4a199e747..8ed969fe9 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -63,6 +63,7 @@ libc_common_src_files := \ stdio/sprintf.c \ stdio/stdio.c \ stdio/stdio_ext.cpp \ + stdlib/atexit.c \ stdlib/exit.c \ # Fortify implementations of libc functions. @@ -481,7 +482,6 @@ libc_upstream_openbsd_ndk_src_files := \ upstream-openbsd/lib/libc/stdio/wprintf.c \ upstream-openbsd/lib/libc/stdio/wscanf.c \ upstream-openbsd/lib/libc/stdio/wsetup.c \ - upstream-openbsd/lib/libc/stdlib/atexit.c \ upstream-openbsd/lib/libc/stdlib/atoi.c \ upstream-openbsd/lib/libc/stdlib/atol.c \ upstream-openbsd/lib/libc/stdlib/atoll.c \ diff --git a/libc/arch-arm64/bionic/crtbegin.c b/libc/arch-arm64/bionic/crtbegin.c index fec0b11af..7e2c5d766 100644 --- a/libc/arch-arm64/bionic/crtbegin.c +++ b/libc/arch-arm64/bionic/crtbegin.c @@ -67,3 +67,4 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" +#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c index fa9f3f32b..c46405c4f 100644 --- a/libc/arch-common/bionic/crtbegin.c +++ b/libc/arch-common/bionic/crtbegin.c @@ -59,6 +59,7 @@ void _start() { #include "__dso_handle.h" #include "atexit.h" +#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/crtbegin_so.c b/libc/arch-common/bionic/crtbegin_so.c index 641e45a4e..3754363ab 100644 --- a/libc/arch-common/bionic/crtbegin_so.c +++ b/libc/arch-common/bionic/crtbegin_so.c @@ -56,6 +56,7 @@ void __on_dlclose() { # include "__dso_handle_so.h" # include "atexit.h" #endif +#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/pthread_atfork.h b/libc/arch-common/bionic/pthread_atfork.h new file mode 100644 index 000000000..0c48a1269 --- /dev/null +++ b/libc/arch-common/bionic/pthread_atfork.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern void* __dso_handle; + +extern int __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void), void* dso); + +#ifndef _LIBC +// Libc used to export this in previous versions, therefore it needs +// to remain global for binary compatibility. +__attribute__ ((visibility ("hidden"))) +#endif +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { + return __register_atfork(prepare, parent, child, &__dso_handle); +} + diff --git a/libc/arch-mips/bionic/crtbegin.c b/libc/arch-mips/bionic/crtbegin.c index 50e9eeb02..d72ec7b8d 100644 --- a/libc/arch-mips/bionic/crtbegin.c +++ b/libc/arch-mips/bionic/crtbegin.c @@ -92,3 +92,4 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" +#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp index d1c4ad0c4..093ffd240 100644 --- a/libc/bionic/pthread_atfork.cpp +++ b/libc/bionic/pthread_atfork.cpp @@ -30,6 +30,8 @@ #include #include +#include "private/bionic_macros.h" + struct atfork_t { atfork_t* next; atfork_t* prev; @@ -37,79 +39,143 @@ struct atfork_t { void (*prepare)(void); void (*child)(void); void (*parent)(void); + + void* dso_handle; }; -struct atfork_list_t { - atfork_t* first; - atfork_t* last; +class atfork_list_t { + public: + atfork_list_t() : first_(nullptr), last_(nullptr) {} + + template + void walk_forward(F f) { + for (atfork_t* it = first_; it != nullptr; it = it->next) { + f(it); + } + } + + template + void walk_backwards(F f) { + for (atfork_t* it = last_; it != nullptr; it = it->prev) { + f(it); + } + } + + void push_back(atfork_t* entry) { + entry->next = nullptr; + entry->prev = last_; + if (entry->prev != nullptr) { + entry->prev->next = entry; + } + if (first_ == nullptr) { + first_ = entry; + } + last_ = entry; + } + + template + void remove_if(F predicate) { + atfork_t* it = first_; + while (it != nullptr) { + if (predicate(it)) { + atfork_t* entry = it; + it = it->next; + remove(entry); + } else { + it = it->next; + } + } + } + + private: + void remove(atfork_t* entry) { + if (entry->prev != nullptr) { + entry->prev->next = entry->next; + } else { + first_ = entry->next; + } + + if (entry->next != nullptr) { + entry->next->prev = entry->prev; + } else { + last_ = entry->prev; + } + + free(entry); + } + + atfork_t* first_; + atfork_t* last_; + + DISALLOW_COPY_AND_ASSIGN(atfork_list_t); }; static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; -static atfork_list_t g_atfork_list = { NULL, NULL }; +static atfork_list_t g_atfork_list; void __bionic_atfork_run_prepare() { // We lock the atfork list here, unlock it in the parent, and reset it in the child. // This ensures that nobody can modify the handler array between the calls // to the prepare and parent/child handlers. - // - // TODO: If a handler tries to mutate the list, they'll block. We should probably copy - // the list before forking, and have prepare, parent, and child all work on the consistent copy. pthread_mutex_lock(&g_atfork_list_mutex); // Call pthread_atfork() prepare handlers. POSIX states that the prepare // handlers should be called in the reverse order of the parent/child // handlers, so we iterate backwards. - for (atfork_t* it = g_atfork_list.last; it != NULL; it = it->prev) { - if (it->prepare != NULL) { + g_atfork_list.walk_backwards([](atfork_t* it) { + if (it->prepare != nullptr) { it->prepare(); } - } + }); } void __bionic_atfork_run_child() { - for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { - if (it->child != NULL) { + g_atfork_list.walk_forward([](atfork_t* it) { + if (it->child != nullptr) { it->child(); } - } + }); g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; } void __bionic_atfork_run_parent() { - for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { - if (it->parent != NULL) { + g_atfork_list.walk_forward([](atfork_t* it) { + if (it->parent != nullptr) { it->parent(); } - } + }); pthread_mutex_unlock(&g_atfork_list_mutex); } -int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) { +// __register_atfork is the name used by glibc +extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void), + void(*child)(void), void* dso) { atfork_t* entry = reinterpret_cast(malloc(sizeof(atfork_t))); - if (entry == NULL) { + if (entry == nullptr) { return ENOMEM; } entry->prepare = prepare; entry->parent = parent; entry->child = child; + entry->dso_handle = dso; pthread_mutex_lock(&g_atfork_list_mutex); - // Append 'entry' to the list. - entry->next = NULL; - entry->prev = g_atfork_list.last; - if (entry->prev != NULL) { - entry->prev->next = entry; - } - if (g_atfork_list.first == NULL) { - g_atfork_list.first = entry; - } - g_atfork_list.last = entry; + g_atfork_list.push_back(entry); pthread_mutex_unlock(&g_atfork_list_mutex); return 0; } + +extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) { + pthread_mutex_lock(&g_atfork_list_mutex); + g_atfork_list.remove_if([&](const atfork_t* entry) { + return entry->dso_handle == dso; + }); + pthread_mutex_unlock(&g_atfork_list_mutex); +} + diff --git a/libc/upstream-openbsd/lib/libc/stdlib/atexit.c b/libc/stdlib/atexit.c similarity index 92% rename from libc/upstream-openbsd/lib/libc/stdlib/atexit.c rename to libc/stdlib/atexit.c index 6532b382e..df2b1b5d5 100644 --- a/libc/upstream-openbsd/lib/libc/stdlib/atexit.c +++ b/libc/stdlib/atexit.c @@ -35,11 +35,15 @@ #include #include #include "atexit.h" -#include "thread_private.h" +#include "private/thread_private.h" 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 @@ -62,7 +66,7 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso) { struct atexit *p = __atexit; struct atexit_fn *fnp; - int pgsize = getpagesize(); + size_t pgsize = getpagesize(); int ret = -1; if (pgsize < sizeof(*p)) @@ -161,6 +165,12 @@ restart: __atexit = NULL; } _ATEXIT_UNLOCK(); + + /* BEGIN android-changed: call __unregister_atfork if dso is not null */ + if (dso != NULL) { + __unregister_atfork(dso); + } + /* END android-changed */ } /* @@ -170,7 +180,7 @@ void __atexit_register_cleanup(void (*func)(void)) { struct atexit *p; - int pgsize = getpagesize(); + size_t pgsize = getpagesize(); if (pgsize < sizeof(*p)) return; diff --git a/libc/upstream-openbsd/lib/libc/stdlib/atexit.h b/libc/stdlib/atexit.h similarity index 100% rename from libc/upstream-openbsd/lib/libc/stdlib/atexit.h rename to libc/stdlib/atexit.h diff --git a/tests/libs/Android.build.pthread_atfork.mk b/tests/libs/Android.build.pthread_atfork.mk new file mode 100644 index 000000000..72ffec4ec --- /dev/null +++ b/tests/libs/Android.build.pthread_atfork.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ----------------------------------------------------------------------------- +# This library used to test phtread_atfork handler behaviour +# during/after dlclose. +# ----------------------------------------------------------------------------- +libtest_pthread_atfork_src_files := pthread_atfork.cpp + +module := libtest_pthread_atfork +include $(LOCAL_PATH)/Android.build.testlib.mk + diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 3d5b060de..c78661e83 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -25,6 +25,7 @@ common_additional_dependencies := \ $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ + $(LOCAL_PATH)/Android.build.pthread_atfork.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ $(LOCAL_PATH)/Android.build.versioned_lib.mk \ $(TEST_PATH)/Android.build.mk @@ -203,6 +204,11 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk # ----------------------------------------------------------------------------- include $(LOCAL_PATH)/Android.build.versioned_lib.mk +# ----------------------------------------------------------------------------- +# Build libraries needed by pthread_atfork tests +# ----------------------------------------------------------------------------- +include $(LOCAL_PATH)/Android.build.pthread_atfork.mk + # ----------------------------------------------------------------------------- # Library with dependency loop used by dlfcn tests # diff --git a/tests/libs/pthread_atfork.cpp b/tests/libs/pthread_atfork.cpp new file mode 100644 index 000000000..3a5aa4faf --- /dev/null +++ b/tests/libs/pthread_atfork.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +extern "C" int proxy_pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { + return pthread_atfork(prepare, parent, child); +} diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index a299f02f7..b8cfd565b 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -987,14 +988,14 @@ TEST(pthread, pthread_once_1934122) { } static int g_atfork_prepare_calls = 0; -static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 1; } -static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 2; } +static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 1; } +static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 2; } static int g_atfork_parent_calls = 0; -static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 1; } -static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 2; } +static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 1; } +static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 2; } static int g_atfork_child_calls = 0; -static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 1; } -static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 2; } +static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 1; } +static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 2; } TEST(pthread, pthread_atfork_smoke) { ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); @@ -1005,13 +1006,71 @@ TEST(pthread, pthread_atfork_smoke) { // Child and parent calls are made in the order they were registered. if (pid == 0) { - ASSERT_EQ(0x12, g_atfork_child_calls); + ASSERT_EQ(12, g_atfork_child_calls); _exit(0); } - ASSERT_EQ(0x12, g_atfork_parent_calls); + ASSERT_EQ(12, g_atfork_parent_calls); // Prepare calls are made in the reverse order. - ASSERT_EQ(0x21, g_atfork_prepare_calls); + ASSERT_EQ(21, g_atfork_prepare_calls); + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); +} + +static void AtForkPrepare3() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 3; } +static void AtForkPrepare4() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 4; } + +static void AtForkParent3() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 3; } +static void AtForkParent4() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 4; } + +static void AtForkChild3() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 3; } +static void AtForkChild4() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 4; } + +TEST(pthread, pthread_atfork_with_dlclose) { + ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); + + void* handle = dlopen("libtest_pthread_atfork.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(void (*)(void), void (*)(void), void (*)(void)); + fn_t fn = reinterpret_cast(dlsym(handle, "proxy_pthread_atfork")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + // the library registers 2 additional atfork handlers in a constructor + ASSERT_EQ(0, fn(AtForkPrepare2, AtForkParent2, AtForkChild2)); + ASSERT_EQ(0, fn(AtForkPrepare3, AtForkParent3, AtForkChild3)); + + ASSERT_EQ(0, pthread_atfork(AtForkPrepare4, AtForkParent4, AtForkChild4)); + + int pid = fork(); + + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + ASSERT_EQ(1234, g_atfork_child_calls); + _exit(0); + } + + ASSERT_EQ(1234, g_atfork_parent_calls); + ASSERT_EQ(4321, g_atfork_prepare_calls); + + EXPECT_EQ(0, dlclose(handle)); + g_atfork_prepare_calls = g_atfork_parent_calls = g_atfork_child_calls = 0; + + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + + pid = fork(); + + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + ASSERT_EQ(14, g_atfork_child_calls); + _exit(0); + } + + ASSERT_EQ(14, g_atfork_parent_calls); + ASSERT_EQ(41, g_atfork_prepare_calls); + + ASSERT_EQ(pid, waitpid(pid, &status, 0)); } TEST(pthread, pthread_attr_getscope) { From 3391a9ff139d57fe4f8a2ff2d81a5ddc230a6208 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 22 Apr 2015 21:40:38 -0700 Subject: [PATCH 103/123] Simplify close(2) EINTR handling. This doesn't affect code like Chrome that correctly ignores EINTR on close, makes code that tries TEMP_FAILURE_RETRY work (where before it might have closed a different fd and appeared to succeed, or had a bogus EBADF), and makes "goto fail" code work (instead of mistakenly assuming that EINTR means that the close failed). Who loses? Anyone actively trying to detect that they caught a signal while in close(2). I don't think those people exist, and I think they have better alternatives available. Bug: https://code.google.com/p/chromium/issues/detail?id=269623 Bug: http://b/20501816 Change-Id: I11e2f66532fe5d1b0082b2433212e24bdda8219b --- libc/Android.mk | 1 + libc/SYSCALLS.TXT | 2 +- .../arch-arm/syscalls/{close.S => ___close.S} | 5 +- .../syscalls/{close.S => ___close.S} | 5 +- .../syscalls/{close.S => ___close.S} | 5 +- .../syscalls/{close.S => ___close.S} | 5 +- .../arch-x86/syscalls/{close.S => ___close.S} | 5 +- .../syscalls/{close.S => ___close.S} | 5 +- libc/bionic/close.cpp | 56 +++++++++++++++++++ 9 files changed, 76 insertions(+), 13 deletions(-) rename libc/arch-arm/syscalls/{close.S => ___close.S} (84%) rename libc/arch-arm64/syscalls/{close.S => ___close.S} (82%) rename libc/arch-mips/syscalls/{close.S => ___close.S} (85%) rename libc/arch-mips64/syscalls/{close.S => ___close.S} (88%) rename libc/arch-x86/syscalls/{close.S => ___close.S} (89%) rename libc/arch-x86_64/syscalls/{close.S => ___close.S} (84%) create mode 100644 libc/bionic/close.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 4a199e747..5ed9ae49f 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -109,6 +109,7 @@ libc_bionic_ndk_src_files := \ bionic/clock_getcpuclockid.cpp \ bionic/clock_nanosleep.cpp \ bionic/clone.cpp \ + bionic/close.cpp \ bionic/__cmsg_nxthdr.cpp \ bionic/connect.cpp \ bionic/ctype.cpp \ diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index b91f5bf57..33e30eb6e 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -95,7 +95,7 @@ ssize_t pread64(int, void*, size_t, off64_t) arm,mips,x86 ssize_t pread64|pread(int, void*, size_t, off_t) arm64,mips64,x86_64 ssize_t pwrite64(int, void*, size_t, off64_t) arm,mips,x86 ssize_t pwrite64|pwrite(int, void*, size_t, off_t) arm64,mips64,x86_64 -int close(int) all +int ___close:close(int) all pid_t __getpid:getpid() all int munmap(void*, size_t) all void* mremap(void*, size_t, size_t, unsigned long) all diff --git a/libc/arch-arm/syscalls/close.S b/libc/arch-arm/syscalls/___close.S similarity index 84% rename from libc/arch-arm/syscalls/close.S rename to libc/arch-arm/syscalls/___close.S index ec0544565..db8a23036 100644 --- a/libc/arch-arm/syscalls/close.S +++ b/libc/arch-arm/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) mov ip, r7 ldr r7, =__NR_close swi #0 @@ -11,4 +11,5 @@ ENTRY(close) bxls lr neg r0, r0 b __set_errno_internal -END(close) +END(___close) +.hidden ___close diff --git a/libc/arch-arm64/syscalls/close.S b/libc/arch-arm64/syscalls/___close.S similarity index 82% rename from libc/arch-arm64/syscalls/close.S rename to libc/arch-arm64/syscalls/___close.S index 3624581cb..8fb836110 100644 --- a/libc/arch-arm64/syscalls/close.S +++ b/libc/arch-arm64/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) mov x8, __NR_close svc #0 @@ -11,4 +11,5 @@ ENTRY(close) b.hi __set_errno_internal ret -END(close) +END(___close) +.hidden ___close diff --git a/libc/arch-mips/syscalls/close.S b/libc/arch-mips/syscalls/___close.S similarity index 85% rename from libc/arch-mips/syscalls/close.S rename to libc/arch-mips/syscalls/___close.S index 231f497d3..356cfd674 100644 --- a/libc/arch-mips/syscalls/close.S +++ b/libc/arch-mips/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) .set noreorder .cpload t9 li v0, __NR_close @@ -16,4 +16,5 @@ ENTRY(close) j t9 nop .set reorder -END(close) +END(___close) +.hidden ___close diff --git a/libc/arch-mips64/syscalls/close.S b/libc/arch-mips64/syscalls/___close.S similarity index 88% rename from libc/arch-mips64/syscalls/close.S rename to libc/arch-mips64/syscalls/___close.S index 5e237dd42..f1ce708cb 100644 --- a/libc/arch-mips64/syscalls/close.S +++ b/libc/arch-mips64/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) .set push .set noreorder li v0, __NR_close @@ -22,4 +22,5 @@ ENTRY(close) j t9 move ra, t0 .set pop -END(close) +END(___close) +.hidden ___close diff --git a/libc/arch-x86/syscalls/close.S b/libc/arch-x86/syscalls/___close.S similarity index 89% rename from libc/arch-x86/syscalls/close.S rename to libc/arch-x86/syscalls/___close.S index f6cce62cf..796944b64 100644 --- a/libc/arch-x86/syscalls/close.S +++ b/libc/arch-x86/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) pushl %ebx .cfi_def_cfa_offset 8 .cfi_rel_offset ebx, 0 @@ -18,4 +18,5 @@ ENTRY(close) 1: popl %ebx ret -END(close) +END(___close) +.hidden ___close diff --git a/libc/arch-x86_64/syscalls/close.S b/libc/arch-x86_64/syscalls/___close.S similarity index 84% rename from libc/arch-x86_64/syscalls/close.S rename to libc/arch-x86_64/syscalls/___close.S index 8a7ada108..8607f0574 100644 --- a/libc/arch-x86_64/syscalls/close.S +++ b/libc/arch-x86_64/syscalls/___close.S @@ -2,7 +2,7 @@ #include -ENTRY(close) +ENTRY(___close) movl $__NR_close, %eax syscall cmpq $-MAX_ERRNO, %rax @@ -12,4 +12,5 @@ ENTRY(close) call __set_errno_internal 1: ret -END(close) +END(___close) +.hidden ___close diff --git a/libc/bionic/close.cpp b/libc/bionic/close.cpp new file mode 100644 index 000000000..18225f015 --- /dev/null +++ b/libc/bionic/close.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 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 +#include + +extern "C" int ___close(int); + +int close(int fd) { + int rc = ___close(fd); + if (rc == -1 && errno == EINTR) { + // POSIX says that if close returns with EINTR, the fd must not be closed. + // Linus disagrees: http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + // The future POSIX solution is posix_close (http://austingroupbugs.net/view.php?id=529), + // with the state after EINTR being undefined, and EINPROGRESS for the case where close + // was interrupted by a signal but the file descriptor was actually closed. + // My concern with that future behavior is that it breaks existing code that assumes + // that close only returns -1 if it failed. Unlike other system calls, I have real + // difficulty even imagining a caller that would need to know that close was interrupted + // but succeeded. So returning EINTR is wrong (because Linux always closes) and EINPROGRESS + // is harmful because callers need to be rewritten to understand that EINPROGRESS isn't + // actually a failure, but will be reported as one. + + // We don't restore errno because that would incur a cost (the TLS read) for every caller. + // Since callers don't know ahead of time whether close will legitimately fail, they need + // to have stashed the old errno value anyway if they plan on using it afterwards, so + // us clobbering errno here doesn't change anything in that respect. + return 0; + } + return rc; +} From 7280e507b61851bce9b18259368d65b9e950c330 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 23 Apr 2015 12:24:43 -0700 Subject: [PATCH 104/123] Remove jemalloc.a from libc_nomalloc.a Change-Id: I86edc1a6cf3a26c46e6daef2c859459c1b0f29af --- libc/Android.mk | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libc/Android.mk b/libc/Android.mk index 8ed969fe9..8b3498edc 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1221,10 +1221,6 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \ LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi -ifneq ($(MALLOC_IMPL),dlmalloc) -LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc -endif - LOCAL_CXX_STL := none LOCAL_SYSTEM_SHARED_LIBRARIES := @@ -1317,6 +1313,11 @@ LOCAL_MODULE := libc LOCAL_CLANG := $(use_clang) LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) LOCAL_WHOLE_STATIC_LIBRARIES := libc_common + +ifneq ($(MALLOC_IMPL),dlmalloc) +LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc +endif + LOCAL_CXX_STL := none LOCAL_SYSTEM_SHARED_LIBRARIES := LOCAL_ADDRESS_SANITIZER := false @@ -1364,6 +1365,11 @@ LOCAL_STRIP_MODULE := keep_symbols LOCAL_SHARED_LIBRARIES := libdl LOCAL_WHOLE_STATIC_LIBRARIES := libc_common + +ifneq ($(MALLOC_IMPL),dlmalloc) +LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc +endif + LOCAL_CXX_STL := none LOCAL_SYSTEM_SHARED_LIBRARIES := From 0a93df369cf3f44d40ca1f0cb4a58aeab42fb39c Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Thu, 23 Apr 2015 14:27:10 -0700 Subject: [PATCH 105/123] Fix opcode to compile with both gcc and llvm. BUG: 17302991 Change-Id: I31febd9ad24312388068803ce247b295bd73b607 --- libc/Android.mk | 9 --------- libc/arch-x86_64/string/ssse3-strcmp-slm.S | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/libc/Android.mk b/libc/Android.mk index 4a199e747..912be53bd 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -938,9 +938,6 @@ LOCAL_SRC_FILES := $(libc_bionic_src_files) LOCAL_CFLAGS := $(libc_common_cflags) \ -Wframe-larger-than=2048 \ -# ssse3-strcmp-slm.S does not compile with Clang. -LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as - # memcpy.S, memchr.S, etc. do not compile with Clang. LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as @@ -972,9 +969,6 @@ LOCAL_SRC_FILES := $(libc_bionic_ndk_src_files) LOCAL_CFLAGS := $(libc_common_cflags) \ -Wframe-larger-than=2048 \ -# ssse3-strcmp-slm.S does not compile with Clang. -LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as - # memcpy.S, memchr.S, etc. do not compile with Clang. LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as @@ -1027,9 +1021,6 @@ LOCAL_SRC_FILES := $(libc_pthread_src_files) LOCAL_CFLAGS := $(libc_common_cflags) \ -Wframe-larger-than=2048 \ -# ssse3-strcmp-slm.S does not compile with Clang. -LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as - # memcpy.S, memchr.S, etc. do not compile with Clang. LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as diff --git a/libc/arch-x86_64/string/ssse3-strcmp-slm.S b/libc/arch-x86_64/string/ssse3-strcmp-slm.S index 0dd8c275b..e8acd5ba4 100644 --- a/libc/arch-x86_64/string/ssse3-strcmp-slm.S +++ b/libc/arch-x86_64/string/ssse3-strcmp-slm.S @@ -1897,8 +1897,8 @@ L(strcmp_exitz): .p2align 4 L(Byte0): - movzx (%rsi), %ecx - movzx (%rdi), %eax + movzbl (%rsi), %ecx + movzbl (%rdi), %eax sub %ecx, %eax ret From adfcb9731755d6150275bb1729e4501e07f7a4f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 23 Apr 2015 13:47:39 -0700 Subject: [PATCH 106/123] Exit normally when packing relocs saves no space. Bug: http://b/18051137 Change-Id: I43ea5678a677e5d39fb54fafcf3a2f3a252c79b0 --- tools/relocation_packer/src/elf_file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index 11af9ed97..fb74233d2 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -666,7 +666,7 @@ bool ElfFile::PackTypedRelocations(std::vector* relocat if (hole_size <= 0) { LOG(INFO) << "Packing relocations saves no space"; - return false; + return true; } size_t data_padding_bytes = is_padding_relocations_ ? From 094f58fb2a57d1ed5736ae3588bf0355618f915b Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Fri, 24 Apr 2015 03:45:59 +0000 Subject: [PATCH 107/123] Revert "Unregister pthread_atfork handlers on dlclose()" The visibility control in pthread_atfork.h is incorrect. It breaks 64bit libc.so by hiding pthread_atfork. This reverts commit 6df122f8528f9b9fcf7dfea14ae98b0ef66274e1. Change-Id: I21e4b344d500c6f6de0ccb7420b916c4e233dd34 --- libc/Android.mk | 2 +- libc/arch-arm64/bionic/crtbegin.c | 1 - libc/arch-common/bionic/crtbegin.c | 1 - libc/arch-common/bionic/crtbegin_so.c | 1 - libc/arch-common/bionic/pthread_atfork.h | 29 ----- libc/arch-mips/bionic/crtbegin.c | 1 - libc/bionic/pthread_atfork.cpp | 122 ++++-------------- .../lib/libc}/stdlib/atexit.c | 16 +-- .../lib/libc}/stdlib/atexit.h | 0 tests/libs/Android.build.pthread_atfork.mk | 25 ---- tests/libs/Android.mk | 6 - tests/libs/pthread_atfork.cpp | 21 --- tests/pthread_test.cpp | 77 ++--------- 13 files changed, 41 insertions(+), 261 deletions(-) delete mode 100644 libc/arch-common/bionic/pthread_atfork.h rename libc/{ => upstream-openbsd/lib/libc}/stdlib/atexit.c (92%) rename libc/{ => upstream-openbsd/lib/libc}/stdlib/atexit.h (100%) delete mode 100644 tests/libs/Android.build.pthread_atfork.mk delete mode 100644 tests/libs/pthread_atfork.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 8ed969fe9..4a199e747 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -63,7 +63,6 @@ libc_common_src_files := \ stdio/sprintf.c \ stdio/stdio.c \ stdio/stdio_ext.cpp \ - stdlib/atexit.c \ stdlib/exit.c \ # Fortify implementations of libc functions. @@ -482,6 +481,7 @@ libc_upstream_openbsd_ndk_src_files := \ upstream-openbsd/lib/libc/stdio/wprintf.c \ upstream-openbsd/lib/libc/stdio/wscanf.c \ upstream-openbsd/lib/libc/stdio/wsetup.c \ + upstream-openbsd/lib/libc/stdlib/atexit.c \ upstream-openbsd/lib/libc/stdlib/atoi.c \ upstream-openbsd/lib/libc/stdlib/atol.c \ upstream-openbsd/lib/libc/stdlib/atoll.c \ diff --git a/libc/arch-arm64/bionic/crtbegin.c b/libc/arch-arm64/bionic/crtbegin.c index 7e2c5d766..fec0b11af 100644 --- a/libc/arch-arm64/bionic/crtbegin.c +++ b/libc/arch-arm64/bionic/crtbegin.c @@ -67,4 +67,3 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" -#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c index c46405c4f..fa9f3f32b 100644 --- a/libc/arch-common/bionic/crtbegin.c +++ b/libc/arch-common/bionic/crtbegin.c @@ -59,7 +59,6 @@ void _start() { #include "__dso_handle.h" #include "atexit.h" -#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/crtbegin_so.c b/libc/arch-common/bionic/crtbegin_so.c index 3754363ab..641e45a4e 100644 --- a/libc/arch-common/bionic/crtbegin_so.c +++ b/libc/arch-common/bionic/crtbegin_so.c @@ -56,7 +56,6 @@ void __on_dlclose() { # include "__dso_handle_so.h" # include "atexit.h" #endif -#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/pthread_atfork.h b/libc/arch-common/bionic/pthread_atfork.h deleted file mode 100644 index 0c48a1269..000000000 --- a/libc/arch-common/bionic/pthread_atfork.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -extern void* __dso_handle; - -extern int __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void), void* dso); - -#ifndef _LIBC -// Libc used to export this in previous versions, therefore it needs -// to remain global for binary compatibility. -__attribute__ ((visibility ("hidden"))) -#endif -int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { - return __register_atfork(prepare, parent, child, &__dso_handle); -} - diff --git a/libc/arch-mips/bionic/crtbegin.c b/libc/arch-mips/bionic/crtbegin.c index d72ec7b8d..50e9eeb02 100644 --- a/libc/arch-mips/bionic/crtbegin.c +++ b/libc/arch-mips/bionic/crtbegin.c @@ -92,4 +92,3 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" -#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp index 093ffd240..d1c4ad0c4 100644 --- a/libc/bionic/pthread_atfork.cpp +++ b/libc/bionic/pthread_atfork.cpp @@ -30,8 +30,6 @@ #include #include -#include "private/bionic_macros.h" - struct atfork_t { atfork_t* next; atfork_t* prev; @@ -39,143 +37,79 @@ struct atfork_t { void (*prepare)(void); void (*child)(void); void (*parent)(void); - - void* dso_handle; }; -class atfork_list_t { - public: - atfork_list_t() : first_(nullptr), last_(nullptr) {} - - template - void walk_forward(F f) { - for (atfork_t* it = first_; it != nullptr; it = it->next) { - f(it); - } - } - - template - void walk_backwards(F f) { - for (atfork_t* it = last_; it != nullptr; it = it->prev) { - f(it); - } - } - - void push_back(atfork_t* entry) { - entry->next = nullptr; - entry->prev = last_; - if (entry->prev != nullptr) { - entry->prev->next = entry; - } - if (first_ == nullptr) { - first_ = entry; - } - last_ = entry; - } - - template - void remove_if(F predicate) { - atfork_t* it = first_; - while (it != nullptr) { - if (predicate(it)) { - atfork_t* entry = it; - it = it->next; - remove(entry); - } else { - it = it->next; - } - } - } - - private: - void remove(atfork_t* entry) { - if (entry->prev != nullptr) { - entry->prev->next = entry->next; - } else { - first_ = entry->next; - } - - if (entry->next != nullptr) { - entry->next->prev = entry->prev; - } else { - last_ = entry->prev; - } - - free(entry); - } - - atfork_t* first_; - atfork_t* last_; - - DISALLOW_COPY_AND_ASSIGN(atfork_list_t); +struct atfork_list_t { + atfork_t* first; + atfork_t* last; }; static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; -static atfork_list_t g_atfork_list; +static atfork_list_t g_atfork_list = { NULL, NULL }; void __bionic_atfork_run_prepare() { // We lock the atfork list here, unlock it in the parent, and reset it in the child. // This ensures that nobody can modify the handler array between the calls // to the prepare and parent/child handlers. + // + // TODO: If a handler tries to mutate the list, they'll block. We should probably copy + // the list before forking, and have prepare, parent, and child all work on the consistent copy. pthread_mutex_lock(&g_atfork_list_mutex); // Call pthread_atfork() prepare handlers. POSIX states that the prepare // handlers should be called in the reverse order of the parent/child // handlers, so we iterate backwards. - g_atfork_list.walk_backwards([](atfork_t* it) { - if (it->prepare != nullptr) { + for (atfork_t* it = g_atfork_list.last; it != NULL; it = it->prev) { + if (it->prepare != NULL) { it->prepare(); } - }); + } } void __bionic_atfork_run_child() { - g_atfork_list.walk_forward([](atfork_t* it) { - if (it->child != nullptr) { + for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { + if (it->child != NULL) { it->child(); } - }); + } g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; } void __bionic_atfork_run_parent() { - g_atfork_list.walk_forward([](atfork_t* it) { - if (it->parent != nullptr) { + for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { + if (it->parent != NULL) { it->parent(); } - }); + } pthread_mutex_unlock(&g_atfork_list_mutex); } -// __register_atfork is the name used by glibc -extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void), - void(*child)(void), void* dso) { +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) { atfork_t* entry = reinterpret_cast(malloc(sizeof(atfork_t))); - if (entry == nullptr) { + if (entry == NULL) { return ENOMEM; } entry->prepare = prepare; entry->parent = parent; entry->child = child; - entry->dso_handle = dso; pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_list.push_back(entry); + // Append 'entry' to the list. + entry->next = NULL; + entry->prev = g_atfork_list.last; + if (entry->prev != NULL) { + entry->prev->next = entry; + } + if (g_atfork_list.first == NULL) { + g_atfork_list.first = entry; + } + g_atfork_list.last = entry; pthread_mutex_unlock(&g_atfork_list_mutex); return 0; } - -extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) { - pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_list.remove_if([&](const atfork_t* entry) { - return entry->dso_handle == dso; - }); - pthread_mutex_unlock(&g_atfork_list_mutex); -} - diff --git a/libc/stdlib/atexit.c b/libc/upstream-openbsd/lib/libc/stdlib/atexit.c similarity index 92% rename from libc/stdlib/atexit.c rename to libc/upstream-openbsd/lib/libc/stdlib/atexit.c index df2b1b5d5..6532b382e 100644 --- a/libc/stdlib/atexit.c +++ b/libc/upstream-openbsd/lib/libc/stdlib/atexit.c @@ -35,15 +35,11 @@ #include #include #include "atexit.h" -#include "private/thread_private.h" +#include "thread_private.h" 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 @@ -66,7 +62,7 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso) { struct atexit *p = __atexit; struct atexit_fn *fnp; - size_t pgsize = getpagesize(); + int pgsize = getpagesize(); int ret = -1; if (pgsize < sizeof(*p)) @@ -165,12 +161,6 @@ restart: __atexit = NULL; } _ATEXIT_UNLOCK(); - - /* BEGIN android-changed: call __unregister_atfork if dso is not null */ - if (dso != NULL) { - __unregister_atfork(dso); - } - /* END android-changed */ } /* @@ -180,7 +170,7 @@ void __atexit_register_cleanup(void (*func)(void)) { struct atexit *p; - size_t pgsize = getpagesize(); + int pgsize = getpagesize(); if (pgsize < sizeof(*p)) return; diff --git a/libc/stdlib/atexit.h b/libc/upstream-openbsd/lib/libc/stdlib/atexit.h similarity index 100% rename from libc/stdlib/atexit.h rename to libc/upstream-openbsd/lib/libc/stdlib/atexit.h diff --git a/tests/libs/Android.build.pthread_atfork.mk b/tests/libs/Android.build.pthread_atfork.mk deleted file mode 100644 index 72ffec4ec..000000000 --- a/tests/libs/Android.build.pthread_atfork.mk +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# ----------------------------------------------------------------------------- -# This library used to test phtread_atfork handler behaviour -# during/after dlclose. -# ----------------------------------------------------------------------------- -libtest_pthread_atfork_src_files := pthread_atfork.cpp - -module := libtest_pthread_atfork -include $(LOCAL_PATH)/Android.build.testlib.mk - diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index c78661e83..3d5b060de 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -25,7 +25,6 @@ common_additional_dependencies := \ $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ - $(LOCAL_PATH)/Android.build.pthread_atfork.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ $(LOCAL_PATH)/Android.build.versioned_lib.mk \ $(TEST_PATH)/Android.build.mk @@ -204,11 +203,6 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk # ----------------------------------------------------------------------------- include $(LOCAL_PATH)/Android.build.versioned_lib.mk -# ----------------------------------------------------------------------------- -# Build libraries needed by pthread_atfork tests -# ----------------------------------------------------------------------------- -include $(LOCAL_PATH)/Android.build.pthread_atfork.mk - # ----------------------------------------------------------------------------- # Library with dependency loop used by dlfcn tests # diff --git a/tests/libs/pthread_atfork.cpp b/tests/libs/pthread_atfork.cpp deleted file mode 100644 index 3a5aa4faf..000000000 --- a/tests/libs/pthread_atfork.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -extern "C" int proxy_pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { - return pthread_atfork(prepare, parent, child); -} diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index b8cfd565b..a299f02f7 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -16,7 +16,6 @@ #include -#include #include #include #include @@ -988,14 +987,14 @@ TEST(pthread, pthread_once_1934122) { } static int g_atfork_prepare_calls = 0; -static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 1; } -static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 2; } +static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 1; } +static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 2; } static int g_atfork_parent_calls = 0; -static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 1; } -static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 2; } +static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 1; } +static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 2; } static int g_atfork_child_calls = 0; -static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 1; } -static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 2; } +static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 1; } +static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 2; } TEST(pthread, pthread_atfork_smoke) { ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); @@ -1006,71 +1005,13 @@ TEST(pthread, pthread_atfork_smoke) { // Child and parent calls are made in the order they were registered. if (pid == 0) { - ASSERT_EQ(12, g_atfork_child_calls); + ASSERT_EQ(0x12, g_atfork_child_calls); _exit(0); } - ASSERT_EQ(12, g_atfork_parent_calls); + ASSERT_EQ(0x12, g_atfork_parent_calls); // Prepare calls are made in the reverse order. - ASSERT_EQ(21, g_atfork_prepare_calls); - int status; - ASSERT_EQ(pid, waitpid(pid, &status, 0)); -} - -static void AtForkPrepare3() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 3; } -static void AtForkPrepare4() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 4; } - -static void AtForkParent3() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 3; } -static void AtForkParent4() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 4; } - -static void AtForkChild3() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 3; } -static void AtForkChild4() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 4; } - -TEST(pthread, pthread_atfork_with_dlclose) { - ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); - - void* handle = dlopen("libtest_pthread_atfork.so", RTLD_NOW | RTLD_LOCAL); - ASSERT_TRUE(handle != nullptr) << dlerror(); - typedef int (*fn_t)(void (*)(void), void (*)(void), void (*)(void)); - fn_t fn = reinterpret_cast(dlsym(handle, "proxy_pthread_atfork")); - ASSERT_TRUE(fn != nullptr) << dlerror(); - // the library registers 2 additional atfork handlers in a constructor - ASSERT_EQ(0, fn(AtForkPrepare2, AtForkParent2, AtForkChild2)); - ASSERT_EQ(0, fn(AtForkPrepare3, AtForkParent3, AtForkChild3)); - - ASSERT_EQ(0, pthread_atfork(AtForkPrepare4, AtForkParent4, AtForkChild4)); - - int pid = fork(); - - ASSERT_NE(-1, pid) << strerror(errno); - - if (pid == 0) { - ASSERT_EQ(1234, g_atfork_child_calls); - _exit(0); - } - - ASSERT_EQ(1234, g_atfork_parent_calls); - ASSERT_EQ(4321, g_atfork_prepare_calls); - - EXPECT_EQ(0, dlclose(handle)); - g_atfork_prepare_calls = g_atfork_parent_calls = g_atfork_child_calls = 0; - - int status; - ASSERT_EQ(pid, waitpid(pid, &status, 0)); - - pid = fork(); - - ASSERT_NE(-1, pid) << strerror(errno); - - if (pid == 0) { - ASSERT_EQ(14, g_atfork_child_calls); - _exit(0); - } - - ASSERT_EQ(14, g_atfork_parent_calls); - ASSERT_EQ(41, g_atfork_prepare_calls); - - ASSERT_EQ(pid, waitpid(pid, &status, 0)); + ASSERT_EQ(0x21, g_atfork_prepare_calls); } TEST(pthread, pthread_attr_getscope) { From d2177404e28290064e087ecb2655e5fdcb9057e5 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Fri, 24 Apr 2015 13:56:11 +0100 Subject: [PATCH 108/123] Update to tzdata 2015c Changes affecting future time stamps Egypt's spring-forward transition is at 24:00 on April's last Thursday, not 00:00 on April's last Friday. 2015's transition will therefore be on Thursday, April 30 at 24:00, not Friday, April 24 at 00:00. Similar fixes apply to 2026, 2037, 2043, etc. (Thanks to Steffen Thorsen.) Changes affecting past time stamps The following changes affect some pre-1991 Chile-related time stamps in America/Santiago, Antarctica/Palmer, and Pacific/Easter. The 1910 transition was January 10, not January 1. The 1918 transition was September 10, not September 1. The UTC-4 time observed from 1932 to 1942 is now considered to be standard time, not year-round DST. Santiago observed DST (UTC-3) from 1946-07-15 through 1946-08-31, then reverted to standard time, then switched its time zone to UTC-5 on 1947-04-01. Assume transitions before 1968 were at 00:00, since we have no data saying otherwise. The spring 1988 transition was 1988-10-09, not 1988-10-02. The fall 1990 transition was 1990-03-11, not 1990-03-18. Assume no UTC offset change for Pacific/Easter on 1890-01-01, and omit all transitions on Pacific/Easter from 1942 through 1946 since we have no data suggesting that they existed. One more zone has been turned into a link, as it differed from an existing zone only for older time stamps. As usual, this change affects UTC offsets in pre-1970 time stamps only. The zone's old contents have been moved to the 'backzone' file. The affected zone is America/Montreal. Bug: 20287125 Change-Id: I8512c4e9ab09725395b256aba59ca34a23d1c995 --- libc/zoneinfo/tzdata | Bin 496043 -> 492537 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata index 3661b68309f8dc5da60aaa5fdcdb16d787cd5608..cbb22bc675ecf36e7252e3cfade173a32a554141 100644 GIT binary patch delta 4905 zcma*q4^&jwod@vu?hG>n4u}y52qG#LOiFL?CXU}rZ zd_V6#?tAz5{@pio_Vv(jt3m@yf0&nBnyW>}EZw8Njwyr_~0e3Z2Up%m%cJW$G1-i6v?rLQJ~)Ivl3c>_5h38^X-8XF9V5)#d{B zALnud!aa0#FEW`~sA1aBZ7knqHXza>x5_rM*2o@<96ib^|C?iE1M?g78q1T+y@T~?CH zT!;$hYP2&~VU)QSA-=kL9uDSLs5F*4nY|ceCLnBvt}Z}2Q=po;A6?9Pj5GHj`~hA4 zF*2DEs9_#L_XC=EtO0k~Sc1rzx*C9Nra&$8FnXBxFu`1kC_h~_BZqkr4a_6xWiG=c za|5FNb#)u^m^aYqZ=BvfHVW$lbhQhy%q$cJXkze1G_i38{mgYRKd7s|h+{URi1`JY znK>AE(Ab47&>%igS2v-Uc@Qnkc^EX(9;BKW~q;$ZI-T{#V|7te!;TJ6lM_0 znShJ=JVu!BBQQi)^N`A{LSG}`U!I8YT{Nq8VGOsUg8ylNiuUfqUUKM><044LMs}HwiJCN zcB+29CK6WE&!3I7`Tk=4Arx|j!-Xco8>XK;o(yxiCU%t|j^wlNA##x>B9zO3dN)-11%ay|7!#W|69ARiD$}?NXkD)DJl63t?t$k`B55t zV>NNK3~Nb+Kg!7QC(%Y;$ii@}dv|_IHBrMSlKjKlC@0Q9xX9`A7$L9y1c8reV$Uk1 zk~emsg1mVF?WFZ3jFPuA5VA}Y?+7@^FY8cALU*8(bOvC|O;{XTLr5n)wrXj+=&KEavGHE}HpHEz1e_2sxrZu|w2D|pW+$R<3rTJpOs=pi4^#RU2E4x+xV ziQBIrhkUUe4TLAUmwf5OB>74~bi8pb@+5s|jOVz2oMY_CG@-6U5wV!jOgz0XK&G96ZIvc`Rm2mXtYQ*Sj21V8$=)a@sUN{b z(i1R3G8Q9njX^5m&0ImUlhIE0y@ye+0f=8OF!s9dXwpMQu>QqqbsQtm)lGN+S! zNGJSXH92t=UF6gbjFZ}Ggs;;?eFiegD;rQlPG_Op-MUL!N)Wl;*gTtXyIOM2h91HZ zCJ4uiN-?_R5bmsjaJyc@o|A+{L_cawW_g5DYa`)o)%U1;F>cUA$8N-u%e-_O)^MA^df}}twXt_2QKny5=JE52y|%T z-xH83=|Tnh!wR$$jxZ`)od`+O#PBjWB$rT0ZmmS8q#I-8_8Nq3C0CJ7K1)Wmqz7GO zWDUk8e06Tq#OJG#Dd|QH`Qs{d6ApCOO=LP({tL3n7qO^y)8l4B@-swb#F)%?rXxrC zZ66vkctNw!OGF(eiB|}sA7eF%$Rji7qLGBYhCUJzTmLwx%{;`C=%Xkk%m0Wb!t>fs z9Gqt}ja|l(9U~}mx0(s3vH^F?wp|lX4&s9| z&Sj#NaGyh@<9Yb*&_vgJSW9}NQARi;wGlpL!{qmu;g@afGllSg%E^}&xUyqR9(Ufv zi1e=}1nzXVQ+FDjD~PE8?e5kn@mz$ECp0l*FB~NB15}c^Dd;3T3u9zq5yEyErzU-u zdvDcj#+*bKSrLVClJpkBbNHI-MJC~)){w2dh&h@)E013T_)(PgNignOtI30>Au`uE z8`*@{xi(kx`gk)t@qShmbUPN*C7MlU{%6A7A!@hLFNfszqJi+G(n~niOzxIHGottK zg|`ZMl6z<*drqKlkNnkJLb7)Ynn=Ei{ye#@c`qB|h$Ea^iwNh|=DoVl z^ZZ(POO~`Dw>WpdyMETiN!FIHiMHE_&)50;r8r;nF8C>*&_`t7P21AlJuN27fnQ^g zJ&TvaUSNDDNi2|G5lfkmp_QxnwPA?V4#Brjw{KGv&m(dlj;(9H+lPEJloe{?*(GQr z#}{C@Q1d#mlU&PP)v(QL*;CJMt-SMw1|xUP`!7HuMYDEkj; z-WIc>m@M)fZr!pG#?5J&#uu`ph(&TOW+a3oaj8Y{om}*c@tijk&O`&x$lpn}662?8 ziSebnn2jZ9VV=PtSIwLbd#Q2K5=-STj#B0gv@*A0hd_Jt~$JSx2J`xPqVgC!|hYxs~lFkg{>!Z(^ z?(`31e?^A1<^J%|{7VjJ-3m3+dH(^kKboti;a@9OIfHJQrd_BxmS8rv`S8d8X>9Z1 ziE}1ygXgNM&{!3R`0ADB*dLc_cAB=+o#0aZ2}s zZ!bqX544$jol_wuAB#|hS*XtUwp*T1tP?m^}Ew+?RIpKun&q-o2!lr=~8t6x-C)wQhH+cJ96389dMQke94k z?n6;J(&m|_|Kxujc?XaD2jc%4xk^26;>XO7M~8aelCj@=7k6jnXZpo!iPiu637r#a z9l?yVNy{u#Q!ke(%GA`^W|ft`Qmc45t34RK!CZb@2g{jLy}!vR?i_r`L3zDt(FWtPMB UEXOBRmh*Qdb^KtyHFp1h0XmbP=l}o! delta 5599 zcmZ|T4_H&@y$A4|lZ1c%k8DTxN_(H{!Z3zyH77Z`F`@A zygBE6f9ISxi4S%RK7M3yc+SsU%W{@!@d=}r8~7J)Fl=xdEYpm8!=YIAR%R*|geo>> zijt{(iz?-S0ytC~pXW1yYITfq8SUywWeg&XHsy6VjR)AjozE?ZGD*)=<`EQ_GTFa_ z&w*&OO>rTOxe$d+3%vUCP9_i&An!`ovq;bS06BWBMPAL&vxNCA8uaJgOc&wNuOmh%o?T1&b5&zVB+Zv&)M+^JaBP7^n9>RW$^?pl&Bh2}1ZPZ)E z!4b-Ls0_9#GtkO@oAiEF!QqJS)!+OfTQBSVmiLNK<|D6{O}U6FW-Z#7!3ggyBXKZe zkk7QEnmG|ZrVEiFHs!Z)GTX6Te_qSHhIZxzMD?*L(MV+~C}6&bI_6$vWsX7UKpABUGaPx$ zVpK7=qK#RP@JO4o0uJUjSdir4gyj z-=Tn+iMql1G&|V1jOa&g%2A{-A4efGA6{lWI++S$qU1hHXD&byb3f{tkD`m2f!N1v z%7@5!OjCQrqlEA(Zy^1zpqoS;LHy&I8n+xSGWq~Y$ zsN@SL?x2;NK8Da3x$aV8H1)z`$Riv^6}dVJZT?pHaDAi0PrjdO()LC}jx_S* zmnbB$9q^Jk6FP-Eh#8@&i5HMg@F|MOsEw#6PknBa496tKNQDn>Vh#yBny*~<)@4+x`ih4-CmdyG<9hy z#*)l9WDBh*C+=i4lbmz}YMT1aRwR-2{oy7r-$jMSqxU?C+ke+;)6{$e>}1!A$RS%^ zz)^pzh3vkAkVH*=#ek{8LFAG>U!#(geuCCS|L#IZY3hHBKngiB4tb=a9987aF=!(v z`yzZa2X_q)a_S!P$?5T^CT9l2M=st)ebg^P5jqBM!eHRPjFRJIjQ&})%6 zfh{!(Sz^Fll!-xG(IoOjm?qL-8^%u5)LvJRO&%#kIq6r7W)d+1flq7dpq)q}ES8%L z=|Y8{+a&rttag1~b}}pwIev~3Ry>PLLPZ`5vs_tt7s$3&meq?j)U-G&L^vGqMH14I(%f~$B3M|Qq9Z8_Nkg$w*a+d zOFY`i&fADe<~6kysf4c;kdoP`BW3r{K@No=`Wa03it*n21>M?Znr-|8f9eTSH7 z`rFe9-&aJ=6``K+2wjB7i%rqHWDxGGgz$Y0ggv_n2N6GAU$R_;3ur0fn$$R5KJjMo z3j7?2} z!EfB-2i6k#5;tZ2a?G_w?X?FO75f(4c5of*2kR_ena{&MOY42N)nEwxR=OrTW2cy%z<3t1}cfqfmY#NgwD~_KRb~kyo)^Y zxdT~J)a#Jb{t=l#%*L^9EW zEHZ61$_VS)L>6|BQm!ZF+?9Fg@| zP5t#q*h$qT+5-mgV2;d)d_c#pM`wrL1W*Y}x1IH5e! z^(v~;6N~}MNVJJPcO!hUzwKD8cg`2SLbbo;BmGVza*3u6{tKLB*j#KUaZRWttU@~( z-HE8BdTCOZ`uA4A=F@#pN2XPygE$iqoxu+&C(;O~T1b}iBxY#VOn#BGxa6{(Hd(IO z06sIb3^B{}YNQjM=b~j=kNday60c{20bb|Oy>^q)#8mhyxU%Cbtac@N_S=$SZE9A$7i_Z@tk-YR2vV;#%M%L{_lS|%fdXArwxfn~h zvSt&mtmV(ydTr&m(2r$EGnToRt(Nf{OIpjh}md!fPUH-_hHBo_>+Wi zEDeOa?j~1S5bx&Vng4E_M+yxFqg;N5 zd2?n;frI(OEr@4W9U&XhXJYhTyIKGE&3;M0smlqMo#vP1w@%= zD{z~e_&wa3qu0!yBflDQm^X2hIS(z&>j+t|1$$@nIDeIcElB<06mvn#U9Ycem4x?u z>w2xnb^fro%$1&KN|k%TV(P48YXc^29Ehq7VW;votO+uXdm#f~Zr)`4d|w7u=P$4v zxSX+heHWerXyF69T8LgXr9H@zp zkg^L(M_HT8d#D!f!{@%pDBqFR_1gZezSm2FyN)ak@Kvnyb^Lmw&-cds8y&wHe8YEa zM0>}vKfUTZ-hJF%dC~1V(ejG>w}_pP!~&fjIm*-z#5cUDgev7cV_ zza=UH#PYf96VkxAaGvof65dJpzCjF{~7%-p5MSU>D_G~>g+{l@*# zm%B1Nn|G;0YbGo-eq`LdWI{^rL;lIsbU8gyrYOoZMVYE7(-mdPqD)(qsf#jwQA!|6 z8AK_CF3TZGNyz4hQWx1EwGpL0qSQ!~I*C#%QR*d1%|xl2D76!%exlS+lsbx1OWpft z^tO~#j{i{VDlbTFMX9eSH5R4LqSRWHdW%wXQR*&A?M11-DCa2yEz3^1uZJ>^S`eLaaq>ab8bP4c3gDp5aOtBcfGHRF~Vdj+WH13mBVQ!_nLsfyKe zcbghib0S~;w9w<+s9M9;@)`}|BY02rs;dXmKXo2nYktOaD%dzSuzBG1s`odPJ3Ze8 z8(-_4{H8ZL;%@xVu?Fuj^+A>AcrW8EE)-^yib>}e4+&%u&KV4>S(U-W33&afaQpXL zJZgw>Xb(mHctC^V*)qvI5alh_;V-e-gO5Hk{8c*!T@__Oq6|vZ2evuHm}8ZJ$rc%0 zP47O&WW~tQd6vCq3^mEvYS#2M9#+gIbAZ|Cxm>O+9v<}l=r~4>jw9l@I7U4c>p!-5 zeidr`N#7sFXwaYanBu_NTLaMahPCFKP~*LwKUR!}nv;i(Cx2ulMaqt#BSt<@|BqW8`XkWAfqRY{UGNS*l z!h03ZurN!c(Wn}cuy^t#3rCeDqvF^&DvpiS<*0)Hk+S7%m28nS;E=KX)A(wxgjo_- F|1acsfqwu1 From b0b9338ff8655394311aeef77e2b795e6d8a453d Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 24 Apr 2015 12:39:14 -0700 Subject: [PATCH 109/123] Exit normally when relocations are already packed. Bug: http://b/18051137 Change-Id: Idfffac5fe965e3cdeabe6d3b2dcd8c275c6ae5df --- tools/relocation_packer/src/elf_file.cc | 4 ++-- tools/relocation_packer/src/elf_file_unittest.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index fb74233d2..c8ddde6ca 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -618,8 +618,8 @@ bool ElfFile::PackTypedRelocations(std::vector* relocat typedef typename ELF::Rela Rela; if (has_android_relocations_) { - LOG(ERROR) << "Relocation table is already packed"; - return false; + LOG(INFO) << "Relocation table is already packed"; + return true; } // If no relocations then we have nothing packable. Perhaps diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc index 5271eef87..32f79683f 100644 --- a/tools/relocation_packer/src/elf_file_unittest.cc +++ b/tools/relocation_packer/src/elf_file_unittest.cc @@ -103,8 +103,8 @@ template static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) { relocation_packer::ElfFile elf_file(fileno(packed_relocs_so)); - // Ensure packing fails (already packed). - EXPECT_FALSE(elf_file.PackRelocations()); + // Ensure packing already packed elf-file does not fail the build. + EXPECT_TRUE(elf_file.PackRelocations()); // Unpack golden relocations, and check files are now identical. EXPECT_TRUE(elf_file.UnpackRelocations()); From 6612d7a34768484eb002c07a1c7df1bd85c0997a Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 24 Apr 2015 16:26:03 -0700 Subject: [PATCH 110/123] Enable dlsym_df_1_global test for arm/arm64 Change-Id: I1fdebced93175cb14053e2239e79f97239fc2dc2 --- tests/Android.mk | 2 -- tests/dlfcn_test.cpp | 4 ---- tests/libs/Android.mk | 14 +++++--------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index c94237593..cd65c10be 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -295,9 +295,7 @@ bionic-unit-tests_shared_libraries_target := \ # which bionic does not support. Reenable this once this question is resolved. bionic-unit-tests_clang_target := false -ifneq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH)) bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global -endif module := bionic-unit-tests module_tag := optional diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 1023644c0..6b1f10958 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -626,7 +626,6 @@ TEST(dlfcn, dlopen_nodelete_dt_flags_1) { } TEST(dlfcn, dlsym_df_1_global) { -#if !defined(__arm__) && !defined(__aarch64__) void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW); ASSERT_TRUE(handle != nullptr) << dlerror(); int (*get_answer)(); @@ -634,9 +633,6 @@ TEST(dlfcn, dlsym_df_1_global) { ASSERT_TRUE(get_answer != nullptr) << dlerror(); ASSERT_EQ(42, get_answer()); ASSERT_EQ(0, dlclose(handle)); -#else - GTEST_LOG_(INFO) << "This test does nothing on arm/arm64 (to be reenabled once b/18137520 or b/18130452 are fixed).\n"; -#endif } TEST(dlfcn, dlopen_failure) { diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 3d5b060de..cd6709a59 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -348,17 +348,13 @@ include $(LOCAL_PATH)/Android.build.testlib.mk # Library with DF_1_GLOBAL # ----------------------------------------------------------------------------- libdl_test_df_1_global_src_files := dl_df_1_global.cpp -libdl_test_df_1_global_ldflags := -fuse-ld=bfd -Wl,-z,global +libdl_test_df_1_global_ldflags := -Wl,-z,global +# TODO (dimitry): host ld.gold does not yet support -z global +# remove this line once it is updated. +libdl_test_df_1_global_ldflags_host := -fuse-ld=bfd + module := libdl_test_df_1_global -# TODO: re-enable arm once b/18137520 or b/18130452 are fixed -ifeq ($(filter $(TARGET_ARCH),arm arm64),) include $(LOCAL_PATH)/Android.build.testlib.mk -else -# build it for host only -build_target := SHARED_LIBRARY -build_type := host -include $(TEST_PATH)/Android.build.mk -endif # ----------------------------------------------------------------------------- # Library using symbol from libdl_test_df_1_global From ea295f68f1fae7c701baaa717f67296659d567ac Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 20 Nov 2014 20:47:02 -0800 Subject: [PATCH 111/123] Unregister pthread_atfork handlers on dlclose() Bug: http://b/20339788 Change-Id: I874c87faa377645fa9e0752f4fc166d81fd9ef7e --- libc/Android.mk | 17 ++- libc/arch-arm64/bionic/crtbegin.c | 1 + libc/arch-common/bionic/crtbegin.c | 1 + libc/arch-common/bionic/crtbegin_so.c | 1 + libc/arch-common/bionic/pthread_atfork.h | 29 +++++ libc/arch-mips/bionic/crtbegin.c | 1 + libc/bionic/pthread_atfork.cpp | 122 ++++++++++++++---- .../lib/libc => }/stdlib/atexit.c | 16 ++- .../lib/libc => }/stdlib/atexit.h | 0 tests/libs/Android.build.pthread_atfork.mk | 25 ++++ tests/libs/Android.mk | 6 + tests/libs/pthread_atfork.cpp | 21 +++ tests/pthread_test.cpp | 77 +++++++++-- 13 files changed, 270 insertions(+), 47 deletions(-) create mode 100644 libc/arch-common/bionic/pthread_atfork.h rename libc/{upstream-openbsd/lib/libc => }/stdlib/atexit.c (92%) rename libc/{upstream-openbsd/lib/libc => }/stdlib/atexit.h (100%) create mode 100644 tests/libs/Android.build.pthread_atfork.mk create mode 100644 tests/libs/pthread_atfork.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 2175dc48b..a9fed2c04 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -63,6 +63,7 @@ libc_common_src_files := \ stdio/sprintf.c \ stdio/stdio.c \ stdio/stdio_ext.cpp \ + stdlib/atexit.c \ stdlib/exit.c \ # Fortify implementations of libc functions. @@ -482,7 +483,6 @@ libc_upstream_openbsd_ndk_src_files := \ upstream-openbsd/lib/libc/stdio/wprintf.c \ upstream-openbsd/lib/libc/stdio/wscanf.c \ upstream-openbsd/lib/libc/stdio/wsetup.c \ - upstream-openbsd/lib/libc/stdlib/atexit.c \ upstream-openbsd/lib/libc/stdlib/atoi.c \ upstream-openbsd/lib/libc/stdlib/atol.c \ upstream-openbsd/lib/libc/stdlib/atoll.c \ @@ -1340,10 +1340,13 @@ LOCAL_CPPFLAGS := $(libc_common_cppflags) LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ + arch-common/bionic/crtbegin_so.c \ + arch-common/bionic/crtbrand.S \ $(libc_arch_dynamic_src_files) \ bionic/malloc_debug_common.cpp \ bionic/libc_init_dynamic.cpp \ bionic/NetdClient.cpp \ + arch-common/bionic/crtend_so.S \ LOCAL_MODULE := libc LOCAL_CLANG := $(use_clang) @@ -1388,15 +1391,15 @@ LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags)) $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files)) + +LOCAL_NO_CRT := true +LOCAL_ASFLAGS += $(libc_crt_target_cflags) + # special for arm -LOCAL_NO_CRT_arm := true LOCAL_CFLAGS_arm += -DCRT_LEGACY_WORKAROUND -LOCAL_ASFLAGS_arm += $(libc_crt_target_cflags) LOCAL_SRC_FILES_arm += \ - arch-common/bionic/crtbegin_so.c \ - arch-common/bionic/crtbrand.S \ - arch-arm/bionic/atexit_legacy.c \ - arch-common/bionic/crtend_so.S + arch-arm/bionic/atexit_legacy.c + LOCAL_ADDRESS_SANITIZER := false LOCAL_NATIVE_COVERAGE := $(bionic_coverage) diff --git a/libc/arch-arm64/bionic/crtbegin.c b/libc/arch-arm64/bionic/crtbegin.c index fec0b11af..7e2c5d766 100644 --- a/libc/arch-arm64/bionic/crtbegin.c +++ b/libc/arch-arm64/bionic/crtbegin.c @@ -67,3 +67,4 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" +#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c index fa9f3f32b..c46405c4f 100644 --- a/libc/arch-common/bionic/crtbegin.c +++ b/libc/arch-common/bionic/crtbegin.c @@ -59,6 +59,7 @@ void _start() { #include "__dso_handle.h" #include "atexit.h" +#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/crtbegin_so.c b/libc/arch-common/bionic/crtbegin_so.c index 641e45a4e..3754363ab 100644 --- a/libc/arch-common/bionic/crtbegin_so.c +++ b/libc/arch-common/bionic/crtbegin_so.c @@ -56,6 +56,7 @@ void __on_dlclose() { # include "__dso_handle_so.h" # include "atexit.h" #endif +#include "pthread_atfork.h" #ifdef __i386__ # include "../../arch-x86/bionic/__stack_chk_fail_local.h" #endif diff --git a/libc/arch-common/bionic/pthread_atfork.h b/libc/arch-common/bionic/pthread_atfork.h new file mode 100644 index 000000000..0c48a1269 --- /dev/null +++ b/libc/arch-common/bionic/pthread_atfork.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern void* __dso_handle; + +extern int __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void), void* dso); + +#ifndef _LIBC +// Libc used to export this in previous versions, therefore it needs +// to remain global for binary compatibility. +__attribute__ ((visibility ("hidden"))) +#endif +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { + return __register_atfork(prepare, parent, child, &__dso_handle); +} + diff --git a/libc/arch-mips/bionic/crtbegin.c b/libc/arch-mips/bionic/crtbegin.c index 50e9eeb02..d72ec7b8d 100644 --- a/libc/arch-mips/bionic/crtbegin.c +++ b/libc/arch-mips/bionic/crtbegin.c @@ -92,3 +92,4 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" +#include "../../arch-common/bionic/pthread_atfork.h" diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp index d1c4ad0c4..093ffd240 100644 --- a/libc/bionic/pthread_atfork.cpp +++ b/libc/bionic/pthread_atfork.cpp @@ -30,6 +30,8 @@ #include #include +#include "private/bionic_macros.h" + struct atfork_t { atfork_t* next; atfork_t* prev; @@ -37,79 +39,143 @@ struct atfork_t { void (*prepare)(void); void (*child)(void); void (*parent)(void); + + void* dso_handle; }; -struct atfork_list_t { - atfork_t* first; - atfork_t* last; +class atfork_list_t { + public: + atfork_list_t() : first_(nullptr), last_(nullptr) {} + + template + void walk_forward(F f) { + for (atfork_t* it = first_; it != nullptr; it = it->next) { + f(it); + } + } + + template + void walk_backwards(F f) { + for (atfork_t* it = last_; it != nullptr; it = it->prev) { + f(it); + } + } + + void push_back(atfork_t* entry) { + entry->next = nullptr; + entry->prev = last_; + if (entry->prev != nullptr) { + entry->prev->next = entry; + } + if (first_ == nullptr) { + first_ = entry; + } + last_ = entry; + } + + template + void remove_if(F predicate) { + atfork_t* it = first_; + while (it != nullptr) { + if (predicate(it)) { + atfork_t* entry = it; + it = it->next; + remove(entry); + } else { + it = it->next; + } + } + } + + private: + void remove(atfork_t* entry) { + if (entry->prev != nullptr) { + entry->prev->next = entry->next; + } else { + first_ = entry->next; + } + + if (entry->next != nullptr) { + entry->next->prev = entry->prev; + } else { + last_ = entry->prev; + } + + free(entry); + } + + atfork_t* first_; + atfork_t* last_; + + DISALLOW_COPY_AND_ASSIGN(atfork_list_t); }; static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; -static atfork_list_t g_atfork_list = { NULL, NULL }; +static atfork_list_t g_atfork_list; void __bionic_atfork_run_prepare() { // We lock the atfork list here, unlock it in the parent, and reset it in the child. // This ensures that nobody can modify the handler array between the calls // to the prepare and parent/child handlers. - // - // TODO: If a handler tries to mutate the list, they'll block. We should probably copy - // the list before forking, and have prepare, parent, and child all work on the consistent copy. pthread_mutex_lock(&g_atfork_list_mutex); // Call pthread_atfork() prepare handlers. POSIX states that the prepare // handlers should be called in the reverse order of the parent/child // handlers, so we iterate backwards. - for (atfork_t* it = g_atfork_list.last; it != NULL; it = it->prev) { - if (it->prepare != NULL) { + g_atfork_list.walk_backwards([](atfork_t* it) { + if (it->prepare != nullptr) { it->prepare(); } - } + }); } void __bionic_atfork_run_child() { - for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { - if (it->child != NULL) { + g_atfork_list.walk_forward([](atfork_t* it) { + if (it->child != nullptr) { it->child(); } - } + }); g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; } void __bionic_atfork_run_parent() { - for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) { - if (it->parent != NULL) { + g_atfork_list.walk_forward([](atfork_t* it) { + if (it->parent != nullptr) { it->parent(); } - } + }); pthread_mutex_unlock(&g_atfork_list_mutex); } -int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) { +// __register_atfork is the name used by glibc +extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void), + void(*child)(void), void* dso) { atfork_t* entry = reinterpret_cast(malloc(sizeof(atfork_t))); - if (entry == NULL) { + if (entry == nullptr) { return ENOMEM; } entry->prepare = prepare; entry->parent = parent; entry->child = child; + entry->dso_handle = dso; pthread_mutex_lock(&g_atfork_list_mutex); - // Append 'entry' to the list. - entry->next = NULL; - entry->prev = g_atfork_list.last; - if (entry->prev != NULL) { - entry->prev->next = entry; - } - if (g_atfork_list.first == NULL) { - g_atfork_list.first = entry; - } - g_atfork_list.last = entry; + g_atfork_list.push_back(entry); pthread_mutex_unlock(&g_atfork_list_mutex); return 0; } + +extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) { + pthread_mutex_lock(&g_atfork_list_mutex); + g_atfork_list.remove_if([&](const atfork_t* entry) { + return entry->dso_handle == dso; + }); + pthread_mutex_unlock(&g_atfork_list_mutex); +} + diff --git a/libc/upstream-openbsd/lib/libc/stdlib/atexit.c b/libc/stdlib/atexit.c similarity index 92% rename from libc/upstream-openbsd/lib/libc/stdlib/atexit.c rename to libc/stdlib/atexit.c index 6532b382e..df2b1b5d5 100644 --- a/libc/upstream-openbsd/lib/libc/stdlib/atexit.c +++ b/libc/stdlib/atexit.c @@ -35,11 +35,15 @@ #include #include #include "atexit.h" -#include "thread_private.h" +#include "private/thread_private.h" 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 @@ -62,7 +66,7 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso) { struct atexit *p = __atexit; struct atexit_fn *fnp; - int pgsize = getpagesize(); + size_t pgsize = getpagesize(); int ret = -1; if (pgsize < sizeof(*p)) @@ -161,6 +165,12 @@ restart: __atexit = NULL; } _ATEXIT_UNLOCK(); + + /* BEGIN android-changed: call __unregister_atfork if dso is not null */ + if (dso != NULL) { + __unregister_atfork(dso); + } + /* END android-changed */ } /* @@ -170,7 +180,7 @@ void __atexit_register_cleanup(void (*func)(void)) { struct atexit *p; - int pgsize = getpagesize(); + size_t pgsize = getpagesize(); if (pgsize < sizeof(*p)) return; diff --git a/libc/upstream-openbsd/lib/libc/stdlib/atexit.h b/libc/stdlib/atexit.h similarity index 100% rename from libc/upstream-openbsd/lib/libc/stdlib/atexit.h rename to libc/stdlib/atexit.h diff --git a/tests/libs/Android.build.pthread_atfork.mk b/tests/libs/Android.build.pthread_atfork.mk new file mode 100644 index 000000000..72ffec4ec --- /dev/null +++ b/tests/libs/Android.build.pthread_atfork.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ----------------------------------------------------------------------------- +# This library used to test phtread_atfork handler behaviour +# during/after dlclose. +# ----------------------------------------------------------------------------- +libtest_pthread_atfork_src_files := pthread_atfork.cpp + +module := libtest_pthread_atfork +include $(LOCAL_PATH)/Android.build.testlib.mk + diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index cd6709a59..dfebdd8b4 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -25,6 +25,7 @@ common_additional_dependencies := \ $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ + $(LOCAL_PATH)/Android.build.pthread_atfork.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ $(LOCAL_PATH)/Android.build.versioned_lib.mk \ $(TEST_PATH)/Android.build.mk @@ -203,6 +204,11 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk # ----------------------------------------------------------------------------- include $(LOCAL_PATH)/Android.build.versioned_lib.mk +# ----------------------------------------------------------------------------- +# Build libraries needed by pthread_atfork tests +# ----------------------------------------------------------------------------- +include $(LOCAL_PATH)/Android.build.pthread_atfork.mk + # ----------------------------------------------------------------------------- # Library with dependency loop used by dlfcn tests # diff --git a/tests/libs/pthread_atfork.cpp b/tests/libs/pthread_atfork.cpp new file mode 100644 index 000000000..3a5aa4faf --- /dev/null +++ b/tests/libs/pthread_atfork.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +extern "C" int proxy_pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { + return pthread_atfork(prepare, parent, child); +} diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index a299f02f7..b8cfd565b 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -987,14 +988,14 @@ TEST(pthread, pthread_once_1934122) { } static int g_atfork_prepare_calls = 0; -static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 1; } -static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 2; } +static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 1; } +static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 2; } static int g_atfork_parent_calls = 0; -static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 1; } -static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 2; } +static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 1; } +static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 2; } static int g_atfork_child_calls = 0; -static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 1; } -static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 2; } +static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 1; } +static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 2; } TEST(pthread, pthread_atfork_smoke) { ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); @@ -1005,13 +1006,71 @@ TEST(pthread, pthread_atfork_smoke) { // Child and parent calls are made in the order they were registered. if (pid == 0) { - ASSERT_EQ(0x12, g_atfork_child_calls); + ASSERT_EQ(12, g_atfork_child_calls); _exit(0); } - ASSERT_EQ(0x12, g_atfork_parent_calls); + ASSERT_EQ(12, g_atfork_parent_calls); // Prepare calls are made in the reverse order. - ASSERT_EQ(0x21, g_atfork_prepare_calls); + ASSERT_EQ(21, g_atfork_prepare_calls); + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); +} + +static void AtForkPrepare3() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 3; } +static void AtForkPrepare4() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 4; } + +static void AtForkParent3() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 3; } +static void AtForkParent4() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 4; } + +static void AtForkChild3() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 3; } +static void AtForkChild4() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 4; } + +TEST(pthread, pthread_atfork_with_dlclose) { + ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); + + void* handle = dlopen("libtest_pthread_atfork.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(handle != nullptr) << dlerror(); + typedef int (*fn_t)(void (*)(void), void (*)(void), void (*)(void)); + fn_t fn = reinterpret_cast(dlsym(handle, "proxy_pthread_atfork")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + // the library registers 2 additional atfork handlers in a constructor + ASSERT_EQ(0, fn(AtForkPrepare2, AtForkParent2, AtForkChild2)); + ASSERT_EQ(0, fn(AtForkPrepare3, AtForkParent3, AtForkChild3)); + + ASSERT_EQ(0, pthread_atfork(AtForkPrepare4, AtForkParent4, AtForkChild4)); + + int pid = fork(); + + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + ASSERT_EQ(1234, g_atfork_child_calls); + _exit(0); + } + + ASSERT_EQ(1234, g_atfork_parent_calls); + ASSERT_EQ(4321, g_atfork_prepare_calls); + + EXPECT_EQ(0, dlclose(handle)); + g_atfork_prepare_calls = g_atfork_parent_calls = g_atfork_child_calls = 0; + + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + + pid = fork(); + + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + ASSERT_EQ(14, g_atfork_child_calls); + _exit(0); + } + + ASSERT_EQ(14, g_atfork_parent_calls); + ASSERT_EQ(41, g_atfork_prepare_calls); + + ASSERT_EQ(pid, waitpid(pid, &status, 0)); } TEST(pthread, pthread_attr_getscope) { From e91e66f223950fdc963cd89697541a32a253a0a6 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 24 Apr 2015 21:05:49 -0700 Subject: [PATCH 112/123] Use bfd linker for x86/x86_64 targets ld.gold in current toolchain for x86_64 does not support -z global. Change-Id: Iea2b192f0f0aa998a02adb356fd4eec4e10a1739 --- tests/libs/Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index cd6709a59..8afe17757 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -349,6 +349,10 @@ include $(LOCAL_PATH)/Android.build.testlib.mk # ----------------------------------------------------------------------------- libdl_test_df_1_global_src_files := dl_df_1_global.cpp libdl_test_df_1_global_ldflags := -Wl,-z,global +# TODO (dimitry): x86* toolchain does not support -z global - switch to bfd +ifeq ($(filter $(TARGET_ARCH),x86 x86_64),$(TARGET_ARCH)) +libdl_test_df_1_global_ldflags_target := -fuse-ld=bfd +endif # TODO (dimitry): host ld.gold does not yet support -z global # remove this line once it is updated. libdl_test_df_1_global_ldflags_host := -fuse-ld=bfd From d1aea30b2ade504550f7bb7996c808b9af1c415d Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Sat, 25 Apr 2015 10:05:24 -0700 Subject: [PATCH 113/123] Fix POSIX timer thread naming. Spencer Low points out that we never actually set a name because the constant part of the string was longer than the kernel's maximum, and the kernel rejects long names rather than truncate. Shorten the fixed part of the string while still keeping it meaningful. 9999 POSIX timers should be enough for any process... Bug: https://code.google.com/p/android/issues/detail?id=170089 Change-Id: Ic05f07584c1eac160743519091a540ebbf8d7eb1 --- libc/bionic/posix_timers.cpp | 6 +++--- tests/pthread_test.cpp | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp index bc3aeb21c..c8f71c831 100644 --- a/libc/bionic/posix_timers.cpp +++ b/libc/bionic/posix_timers.cpp @@ -174,10 +174,10 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) { return -1; } - // Give the thread a meaningful name. + // Give the thread a specific meaningful name. // It can't do this itself because the kernel timer isn't created until after it's running. - char name[32]; - snprintf(name, sizeof(name), "POSIX interval timer %d", to_kernel_timer_id(timer)); + char name[16]; // 16 is the kernel-imposed limit. + snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer)); pthread_setname_np(timer->callback_thread, name); *timer_id = timer; diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index a299f02f7..a9d10978b 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -403,7 +403,9 @@ TEST(pthread, pthread_sigmask) { } TEST(pthread, pthread_setname_np__too_long) { - ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "this name is far too long for linux")); + // The limit is 15 characters --- the kernel's buffer is 16, but includes a NUL. + ASSERT_EQ(0, pthread_setname_np(pthread_self(), "123456789012345")); + ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "1234567890123456")); } TEST(pthread, pthread_setname_np__self) { From f327fae69cd13a40f8c0519afa0a9505be830a10 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Sat, 25 Apr 2015 11:59:32 -0700 Subject: [PATCH 114/123] Include pthread_atfork.h to mips64 crtbegin Bug: http://b/20339788 Change-Id: I2a8c7881f90a05ca768cb9b4c2f8b07c74c64469 --- libc/arch-mips64/bionic/crtbegin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/arch-mips64/bionic/crtbegin.c b/libc/arch-mips64/bionic/crtbegin.c index 1374fea35..bdd423b5a 100644 --- a/libc/arch-mips64/bionic/crtbegin.c +++ b/libc/arch-mips64/bionic/crtbegin.c @@ -92,3 +92,4 @@ __asm__ ( #include "../../arch-common/bionic/__dso_handle.h" #include "../../arch-common/bionic/atexit.h" +#include "../../arch-common/bionic/pthread_atfork.h" From bfd65279a5a9018b01f71773270e462f1b9a7768 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Fri, 24 Apr 2015 09:31:32 -0700 Subject: [PATCH 115/123] bionic: add __system_property_area_serial() Adds a new _internal_ function. Provide a global serial number to support more efficient private caching algorithms. This allows to skip re-running the __system_property_find() call on misses until there is a global change in the properties. This call is a read barrier, the property data to be read following this call will be read sequentially and up to date. Bug: 19544788 Change-Id: I58e6a92baa0f3e8e7b9ec79b10af6d56407dab48 --- libc/bionic/system_properties.cpp | 10 ++++++++++ libc/include/sys/_system_properties.h | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp index aae99b12c..c436a16c6 100644 --- a/libc/bionic/system_properties.cpp +++ b/libc/bionic/system_properties.cpp @@ -598,6 +598,16 @@ int __system_property_area_init() return map_prop_area_rw(); } +unsigned int __system_property_area_serial() +{ + prop_area *pa = __system_property_area__; + if (!pa) { + return -1; + } + // Make sure this read fulfilled before __system_property_serial + return atomic_load_explicit(&(pa->serial), memory_order_acquire); +} + const prop_info *__system_property_find(const char *name) { if (__predict_false(compat_mode)) { diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h index 7ff3ded07..c200295fc 100644 --- a/libc/include/sys/_system_properties.h +++ b/libc/include/sys/_system_properties.h @@ -98,6 +98,24 @@ int __system_property_set_filename(const char *filename); */ int __system_property_area_init(); +/* Read the global serial number of the system properties +** +** Called to predict if a series of cached __system_property_find +** objects will have seen __system_property_serial values change. +** But also aids the converse, as changes in the global serial can +** also be used to predict if a failed __system_property_find +** could in-turn now find an new object; thus preventing the +** cycles of effort to poll __system_property_find. +** +** Called at the beginning of a cache cycle to signal _any_ possible +** changes have occurred since last. If there is, check each individual +** __system_property_serial to confirm dirty, or __system_property_find +** to check if the property now exists. +** +** Returns the serial number on success, -1 on error. +*/ +unsigned int __system_property_area_serial(); + /* Add a new system property. Can only be done by a single ** process that has write access to the property area, and ** that process must handle sequencing to ensure the property From 2ea504fed199a63c7f24f7fd3233affe5b6c940f Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 27 Apr 2015 11:16:11 -0700 Subject: [PATCH 116/123] Remove outdated warning It is ok to use malloc in linker. Bug: http://b/20567629 Change-Id: I54183dbe8ebcd223a44e710e511c339688a65dba --- linker/linker.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index be7b10cc1..63e1b174e 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -61,21 +61,6 @@ #include "linker_reloc_iterators.h" #include "ziparchive/zip_archive.h" -/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< - * - * Do NOT use malloc() and friends or pthread_*() code here. - * Don't use printf() either; it's caused mysterious memory - * corruption in the past. - * The linker runs before we bring up libc and it's easiest - * to make sure it does not depend on any complex libc features - * - * open issues / todo: - * - * - cleaner error reporting - * - after linking, set as much stuff as possible to READONLY - * and NOEXEC - */ - // Override macros to use C++ style casts #undef ELF_ST_TYPE #define ELF_ST_TYPE(x) (static_cast(x) & 0xf) From b293969c6d4acede62ac7b035dcb598e63574ca0 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 27 Apr 2015 18:53:27 -0700 Subject: [PATCH 117/123] Reduce p_align for program header to page size. Having p_align > page_size leads to the situation when striping packed executables results in unnecessary p_vaddr adjustments. And it also may result (with probability 1 - 1/sizeof(uintptr_t)) in misaligned segments following .dynstr Bug: http://b/20629834 Bug: http://b/18051137 Change-Id: I1c5da4911e4409d63cb09f6b6b0a16ef54f6501b --- tools/relocation_packer/src/elf_file.cc | 10 ++++++++-- .../elf_file_unittest_relocs_arm64_packed.so | Bin 113651 -> 113651 bytes 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc index c8ddde6ca..6ac9deb33 100644 --- a/tools/relocation_packer/src/elf_file.cc +++ b/tools/relocation_packer/src/elf_file.cc @@ -37,11 +37,13 @@ static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5; static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1; static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2; +static const size_t kPageSize = 4096; + // Alignment to preserve, in bytes. This must be at least as large as the // largest d_align and sh_addralign values found in the loaded file. // Out of caution for RELRO page alignment, we preserve to a complete target // page. See http://www.airs.com/blog/archives/189. -static constexpr size_t kPreserveAlignment = 4096; +static const size_t kPreserveAlignment = kPageSize; // Get section data. Checks that the section has exactly one data entry, // so that the section size and the data size are the same. True in @@ -318,9 +320,13 @@ static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers, } else { program_header->p_vaddr -= hole_size; program_header->p_paddr -= hole_size; + if (program_header->p_align > kPageSize) { + program_header->p_align = kPageSize; + } VLOG(1) << "phdr[" << i << "] p_vaddr adjusted to "<< program_header->p_vaddr - << "; p_paddr adjusted to "<< program_header->p_paddr; + << "; p_paddr adjusted to "<< program_header->p_paddr + << "; p_align adjusted to "<< program_header->p_align; } } } diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so index a2b0039187a95808876f93ef8ccceee0dcd1979c..ed85ce1e77bc80e1f567cee9b632997ce7bff70c 100755 GIT binary patch delta 18 acmezTo$d2?wh4ty0t}7CTZ Date: Tue, 28 Apr 2015 17:03:13 +0100 Subject: [PATCH 118/123] Update to tzdata 2015d Changes affecting future time stamps Egypt will not observe DST in 2015 and will consider canceling it permanently. For now, assume no DST indefinitely. (Thanks to Ahmed Nazmy and Tim Parenti.) Changes affecting past time stamps America/Whitehorse switched from UTC-9 to UTC-8 on 1967-05-28, not 1966-07-01. Also, Yukon's time zone history is documented better. (Thanks to Brian Inglis and Dennis Ferguson.) Change affecting past and future time zone abbreviations The abbreviations for Hawaii-Aleutian standard and daylight times have been changed from HAST/HADT to HST/HDT, as per US Government Printing Office style. This affects only America/Adak since 1983, as America/Honolulu was already using the new style. Bug: 20551453 Change-Id: I02364f15ca4ae20ed1a3b327f8517214bee938e5 --- libc/zoneinfo/tzdata | Bin 492537 -> 491715 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata index cbb22bc675ecf36e7252e3cfade173a32a554141..2c734fa486b19ecefbcfa535550e804a659e8451 100644 GIT binary patch delta 4441 zcmYk<4OEm>8VB&1JHr4n2qxI@p`s3kiU>C%S20nkCIJPV5)*Y$LQ2$8(LhNp*}y`> z`I8Wl%O24sMV*v#wrCG2wycp+VWCSo72A^0hG)spSl$2oPTN_}8Gb%9&wW4cd*A2Y zd$V-Rxo5|i3by5B7i7!WxchQ6{Abc=wx(;m6TtjSB^lAeu8C{GreXi23pAj*8 zNiks-GKpEDoUlQQCmJA3i883ovpiIIXf>#p6z#31*gRAcURj$b8X`P&tdB|#;i0P) ze9^8(4qquI{eXDFW_i9f3i@F+VsZ=WhDkAX4m!!SAK>FB4GCI}#&-ge{9?pxGwgm+ z>59L~^S><ojN#m15HtxJi*fP?&n<7P9jj z6qC~BaFUnO&_g(oOyN@Ok3lM_S%6*S@CY;!p17B=6T?SKaUvG!af2N^x#8GD+JHC@0w|XdxGW!2tQZ9Z?Zde6b!` z#2tYO@@)=UN#8XLlABWyJuXhCy?q$AaeSgXP)T@-He&b%LnPobV#l*$R3nE(evE1o za|i7t?r)9>Qq0slCX7bLgkX_88+rU9Z5wI{hgk=)20(9?B5M|=ljWer8Y zdl&-ar6`_@WQD6JBs=5LNXpD`6CNz+J}LHWfQ2}=qnO}pI7xL8ddNX5ObJrhe342H zMPe7Je+EtDunoQB#8`yiPuh`APA@|V35$e_G;zB05HX!&;s~sSH&#a8`4G*dWe@tv zd;fv?cWUQml5;mu?xBVJRfhrcK?|a0aD-ey7U7Lmkc%N`C6~U(Ai44`qGw8Rbq#E! zX96k-JGqVYW@CtacLA}9>T}2;-yc9V`SC2;N&k7rEVU!z$@NvpBR9sPmJGI{gRsl= z53m$(!Sn~>bh_K#$Y&@nqK9 zO!HJy2}kfQvNQlqB=<%1lC={Mo}4Szom6vwLIBKA$_BSoQzc#w6X6;{IE%E;~t zG?Vg3^pm}dV19@tdn__Z4QB~bZAObH8X$k1g{X(s=2?X8D#+`fpq20pgM{acwx}sK z!h=;3wreBI93s4k*hkbVl|xuFs|hPnJ86-Q`5bN0h$qc_aD>xX?R-yt=wQfNr%zEU z%5*X$kxy8q>Ie%DHr(@C?05^c=!7 zIA>{*u26y!@})Oi3NN6KT=hr9B2ob>aR;DGVIP{wSN`Z%;N-Yiim&~Usqg~I$v3`e zAw1E5hbSw*EJK#UZB%$@^)N_!3K0D$U$o1xksAk4NuD>O?a?@${?0NCF*JOO*vEJ= z!;nJ)zDG3)TZ?uw;kIK53uO%A$<#-XM-rT>G!P!Nn>3{%aG4Z;ei6x} z^%e>V3sEECE9NGj?m^_GgsKRe(g{TtCkZhZ*Tay3d_?n&LnP!oryU&CI8 zUOyvzg%rc5A)N#RC1lK9a1nMxADNhdh?Q#JSP2haMq(DDnIzmmKbgH5W*a9a2QqD* zQ-79((323oO3ky86%JGqP9<%Gb!LcgpAx&8lbkPd6k1VDZ0TqxQEo>LgR6)q*~Q2s zt3pt#@BumqTkD_T-1s)86V}#z!rEH*gba8ojZ4(8l*^i*ZO>k>+=EWf66=%8;?ami z!o80@SNi8H;TxKuWTq{)DtTUfx%2m;iz(}LFs@No9!Z3YzyiXBUIXD;p_}YIg1|iK ze{D4POxu;Cq%WHH>rr%^84B~H*m4t%WXF$i6SfO_k|pnTSO}XJlb2tHlN^Xd52;-W z(^H&iCm~hgC+s4Bs6!Ju)QVnGpMmhTyxUPoS9lX8*LSLbh*KcW3p4`=l(x!ThX~vv{ZHodoVSz{KWhEo zqt>&)6>d@MTqEK8@7^MZxwi2w%1|=VVO8D@Z{{)DiaA`0IkyI%F5bG%Tf5`XwP!?C z+*FZSwb@G_jJi@kYl_l=k+lnA$oO!g@n|iM4uz) z^)n)&BNQ=hE7HjH43rX1X!Aq^ghRp%>hvrRWgglM>M4bLsY9$DDhbc5!xIe??mE(2 z#YVX6Dl#hyooZAuQV|j35KTDEKJvIk?{i#4+(M0yA|kh=i_94Z?@@{&c7{eXG79md zW{V{maEwyKwsO>Z=qAg{U^FUXl|Z~vF|3`g(dbvf-x!FOji&z>I)94w^1_G4F=G85 z^l(Dv`|$Tw4AxLi*q~0>=xZ|A4l&#(LuXK&mk}p@jlw>RdS6BCSPnNQ>^h)MX!JE9 za&(|!_YH=7)%lx8o5Zsg6pmKJflFv4U;F{RT-uE`vaS*Xr0o%eja9_8Kx7e~Vi{qI+R61H z43bahB0NA5pXp&GyrxR>zx&WZzM79=@?A0_$0_3WTG+_lYp5cuQzv;~uLx9xcPpaF zs0`Ri_sggz%g?|?jE_M-UJ-u&h#})^kV}HUMGcu{MHk_|yiF=`Byt5DBx)CGi82}8 zWX@z5CnzHRImAy;43FQ|XtXVGPY7_Xc!q_nnlRe1{ES9p*d@22ZNfycCJ;TGka-CH z6BWbenHr7PCiTaxi4zUmk8*4S8YhlJ`NRprF%R_<6|wtGxJh9Q0v=LNKZzWkfI{*s zppjG@Mla#*WeQS6r41?ML@|m;-7+*29{3hvCWC&dh;unGlk_dH+3xKCFlj-as1ZoPttfJA^j!mnaO78vuE9oTc%h0YbJ0nn zA5=_H#KHv?Q+|onQ^tv9yJ6=Bb0ew=?==@$p9p=ZA~xkAhFFgwmt?z9LwKkza`a(% zPgR6H0&(PtC*dGZno&#gj-s3F8v)}qMeP3*;>n@Y$S0gvN940jUOWZ==}i7rBoOB; z6p)k8p`O&NhMUw)MnIT)$0m`p$52Rk5{;y#0ll8cG(+8(;-SbxGx^|a+#(;2MbJz| zeDn~^#5DrNgg18!VGjEU_Zc!v5&!o%EQC=B87fBWtSFsMIML70w*#T!iU@2$8VSxp zDS6nAHWE^Z0WxVd!XntM=OT-|umNQ*sijp4HuG1~~Mq#l)I&UAE;Yojqt z?%YS@BiwiuHZngORb*iuI?2*y6_JWqb{5fO`Ma=ts3t2{!bSc*68b3KzI;we`fJD~ z8EL2?o8Ltj$?1Z(qKJ2g5J$GX0td-WMJ@ShI=Vd==P2Uo0>qQTZ;(&&zDAuV>LCYK z!#`RP2MdruipHUUJog0Z$%`3q6YeZvt|Cg$Ac_3nurl)JRcI%lPr@Mi;ts<9OA$9u zz)HSNMIgut_>mMSd7WC%JRK;!!mb(PW?sc5*Kj)#UyVT!bmp zFJwP{6EO>;bb9Th$Yp5IqlS$1K^GZy2HuMl;jcv;VU`>u(1BVH-DLb67~|C|iYKNb z-oZsAD-q3P`(@lBJJS)AkRx{XqBubjzyAa+B(DyAq|l6zMD~dxSO|YBAuoJ@ zR`NkPDcq5)Eyhp=Vp^en=8Wu(Cy?Sw}dBs^Ytk~+jn zxU)*ac^!l!hY3$2@^Q6G*$8`P6=5gpBv%j8QeT7F+F-MtyB=Y*8U8+2N}A-g%I z<7F6EDB@2y5x+w5bERoC-go8D#BF;W`52ST!6OV_do&t;XV$Oa*H;{RIR5jM@L#Et zAZbSd`JW}Im$bu8Zajv7Ripz+q~|ddO4`v#zF2}@NgGUNenl)qilh}q?Bl?;8W-%)1xcavsKy;F39$?5sXCk<(+Biv~Zxp)}kP+_D;_=@Z8vEy>k>% ze-m+pYa>UFGHTmPd`#!b!7DK7`gnvSkI@;Px`J*Fam2#7P5u0cC;Sr3C;Zy0Bm7|K zA!R-Aw=2H4*K$p@PwvIMW~t|Z2Az)YhXT7I_CA7o^4wIo2`2_@XXE=4NrdwYN!7<_ zB*!w*OMdeTOi%E2y9p`ex3f?r`3%kEwISRhuOC6slf2>-m?ht%n7nZmE#!A*^mz!$ zpH?658p8h5 zMYzcI&ii?l7@s#=ekeQg)RkH-;hL$NjP-*tUrk;-;ff-kaN$))xD4wdTuS)wR(#J_ z^O!uiMw4p!x!8KPxq{tlx2q@Y0`A?4&pVBLmR8H5a9QLN;>B@{_CR|wvZ-*-4lnIn zr~kHJ986tu#+|zNt}FH27WX>6&Afi{5%+rYN%Q&xF8BJ|MXq$Q-JL!?(UqQBU`_vI zvn%7~a(BkSc~|D-Z>*UO39hX7BHURYXPC3@mAW^)G|jcq6>Z(L!`o#&amt##ZmM~6 zev*63JTJ2?x!<~V+;`@jnSZh7{NrP@-KW`V|Fj|7y#0>L{ltY=%uilF@6J8>rfWx2 zrF-Xrmt0R_kK3{FDc7#NH22dBSG)4oE^y}u&vEUJTW{UtWi~$(6l;CvTg6;3{E&6; z)d2IeH~wke_u5@^;X7Yi_rK6>K2Uq#eK6;3vr{`$q&bIv)Qv-1@hJRoaFH|9r5lC( zq8RPP(+x%XgEC7p4XR9qD$^mADUr&ws4_LGOphv4B$a7WWvWz}E>)&XD$}OQ)JbLf zR2vcwsvBe;oWqzeH$5NSRsm!%h=36RrE|qy#W$vXi|EixJ zY*07II>-&O7E)OcsjP`q)QduvlteI5SO)6_AmGx6y$|A~t z$U;hGF{QGgQdv}~EUZ)(S1JoERf~K%LYof5^V1M}BnIKn&l#VXxYiQBB6{}R+0lepPqjs_-DAdahKOtzkdPd=l93} From e0905c94d37ad8bc0dd259d37866da5d18fb9ccd Mon Sep 17 00:00:00 2001 From: Kyle Repinski Date: Tue, 28 Apr 2015 13:39:41 -0500 Subject: [PATCH 119/123] cortex-a9: Fix reference to __memcpy_base_aligned. With a different memcpy, __memcpy_base_aligned ceased to exist. Instead, point to the name defined by whatever includes memcpy_base.S Change-Id: I242cf49cbada35337ba155d7f170e86a905ff55f --- libc/arch-arm/cortex-a9/bionic/memcpy_base.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/arch-arm/cortex-a9/bionic/memcpy_base.S b/libc/arch-arm/cortex-a9/bionic/memcpy_base.S index 5e813050a..bb08b94e9 100644 --- a/libc/arch-arm/cortex-a9/bionic/memcpy_base.S +++ b/libc/arch-arm/cortex-a9/bionic/memcpy_base.S @@ -44,7 +44,7 @@ ENTRY_PRIVATE(MEMCPY_BASE) /* check if buffers are aligned. If so, run arm-only version */ eor r3, r0, r1 ands r3, r3, #0x3 - beq __memcpy_base_aligned + beq MEMCPY_BASE_ALIGNED /* Check the upper size limit for Neon unaligned memory access in memcpy */ cmp r2, #224 From a85bcc2e99739420b5b4f9a5c337af1d26ac652b Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 28 Apr 2015 13:34:16 -0700 Subject: [PATCH 120/123] linker: use libc's environ variable to store envp This is to make getenv() work correctly. Bug: http://b/20567629 Change-Id: I148627e1efea1649fb0822c95876811652fb4082 --- linker/linker_environ.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/linker/linker_environ.cpp b/linker/linker_environ.cpp index 9a0f0099f..3c466a2f0 100644 --- a/linker/linker_environ.cpp +++ b/linker/linker_environ.cpp @@ -36,7 +36,6 @@ #include "private/KernelArgumentBlock.h" -static char** _envp; static bool _AT_SECURE_value = true; bool get_AT_SECURE() { @@ -150,8 +149,8 @@ static bool __is_unsafe_environment_variable(const char* name) { } static void __sanitize_environment_variables() { - char** src = _envp; - char** dst = _envp; + char** src = environ; + char** dst = environ; for (; src[0] != nullptr; ++src) { if (!__is_valid_environment_variable(src[0])) { continue; @@ -168,7 +167,7 @@ static void __sanitize_environment_variables() { void linker_env_init(KernelArgumentBlock& args) { // Store environment pointer - can't be null. - _envp = args.envp; + environ = args.envp; __init_AT_SECURE(args); __sanitize_environment_variables(); @@ -179,7 +178,7 @@ const char* linker_env_get(const char* name) { return nullptr; } - for (char** p = _envp; p[0] != nullptr; ++p) { + for (char** p = environ; p[0] != nullptr; ++p) { const char* val = env_match(p[0], name); if (val != nullptr) { if (val[0] == '\0') { From 62d6533c1ac4b32568ffcbebb41d480a730eb8bb Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 28 Apr 2015 14:52:47 -0700 Subject: [PATCH 121/123] Do not pack relocations for libc.so Bug: http://b/20645321 Bug: http://b/20655855 (cherry picked from commit 452742d2bf4247106055696c11bc4e59ca630f11) Change-Id: Ic9125cc1bc4c9ba9eb20d030de72e3ce1fb86fa6 --- libc/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libc/Android.mk b/libc/Android.mk index 61f345431..d41f8afa5 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -1352,6 +1352,9 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \ # meaningful name resolution. LOCAL_STRIP_MODULE := keep_symbols +# Do not pack libc.so relocations; see http://b/20645321 for details. +LOCAL_PACK_MODULE_RELOCATIONS := false + # WARNING: The only library libc.so should depend on is libdl.so! If you add other libraries, # make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries. This # ensures that symbols that are pulled into those new libraries from libgcc.a are not declared From f8093a9485402584f75b774ddf2ca051fa9b8aad Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 28 Apr 2015 18:09:53 -0700 Subject: [PATCH 122/123] Fix LD_PRELOAD for dlopen() We did not set DF_1_GLOBAL flag for LD_PRELOADed libraries which led to the situation when ld_preloads where ignored during on dlopen() Change-Id: I696b3b2506a8ed4c0984ad2c803210a7a4f8e686 --- linker/linker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 63e1b174e..2f32c0e3e 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -2429,7 +2429,7 @@ bool soinfo::prelink_image() { /* We can't log anything until the linker is relocated */ bool relocating_linker = (flags_ & FLAG_LINKER) != 0; if (!relocating_linker) { - INFO("[ linking %s ]", get_soname()); + INFO("[ linking %s ]", get_realpath()); DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast(base), flags_); } @@ -3135,6 +3135,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( for (const auto& ld_preload_name : g_ld_preload_names) { needed_library_name_list.push_back(ld_preload_name.c_str()); ++needed_libraries_count; + ++ld_preloads_count; } for_each_dt_needed(si, [&](const char* name) { From 11331f60dd735613eee902b43a02b646ae873032 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 28 Apr 2015 14:35:45 -0700 Subject: [PATCH 123/123] Add float support to binary event log. Bug: 20664753 Change-Id: I6e43c07daa727c19d87f5192bb719af63dd93654 --- libc/bionic/libc_logging.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp index 7ad21c4cd..cb0b334b8 100644 --- a/libc/bionic/libc_logging.cpp +++ b/libc/bionic/libc_logging.cpp @@ -56,6 +56,7 @@ enum AndroidEventLogType { EVENT_TYPE_LONG = 1, EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, + EVENT_TYPE_FLOAT = 4, }; struct BufferOutputStream {