Merge "Make sys_resource test more robust."
diff --git a/ABI-bugs.txt b/ABI-bugs.txt
deleted file mode 100644
index 51da9f0..0000000
--- a/ABI-bugs.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-KNOWN ABI BUGS
---------------
-
- time_t is 32-bit. http://b/5819737
-
- off_t is 32-bit. There is off64_t, but no _FILE_OFFSET_BITS support.
-
- sigset_t is too small on ARM and x86 (but correct on MIPS), so support
- for real-time signals is broken. http://b/5828899
-
- atexit(3) handlers registered by a shared library aren't called on
- dlclose(3); this only affects ARM. http://b/4998315
diff --git a/README.md b/README.md
index a031fbc..2c42b3b 100644
--- a/README.md
+++ b/README.md
@@ -256,3 +256,20 @@
$ genhtml -o covreport coverage.info # or lcov --list coverage.info
The coverage report is now available at `covreport/index.html`.
+
+
+LP32 ABI bugs
+-------------
+
+This probably belongs in the NDK documentation rather than here, but these
+are the known ABI bugs in LP32:
+
+ * `time_t` is 32-bit. <http://b/5819737>
+
+ * `off_t` is 32-bit. There is `off64_t`, but no `_FILE_OFFSET_BITS` support.
+ Many of the `off64_t` functions are missing in older releases, and
+ stdio uses 32-bit offsets, so there's no way to fully implement
+ `_FILE_OFFSET_BITS`.
+
+ * `sigset_t` is too small on ARM and x86 (but correct on MIPS), so support
+ for real-time signals is broken. <http://b/5828899>
diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk
index 5ce8542..8f5fd41 100644
--- a/benchmarks/Android.mk
+++ b/benchmarks/Android.mk
@@ -19,6 +19,27 @@
LOCAL_PATH := $(call my-dir)
# -----------------------------------------------------------------------------
+# Benchmarks library, usable by projects outside this directory.
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbenchmark
+LOCAL_CFLAGS += -O2 -Wall -Wextra -Werror
+LOCAL_SRC_FILES := benchmark_main.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbenchmark
+LOCAL_CFLAGS += -O2 -Wall -Wextra -Werror
+LOCAL_SRC_FILES := benchmark_main.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_MULTILIB := both
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# -----------------------------------------------------------------------------
# Benchmarks.
# -----------------------------------------------------------------------------
@@ -30,7 +51,6 @@
-std=gnu++11 \
benchmark_src_files = \
- benchmark_main.cpp \
math_benchmark.cpp \
pthread_benchmark.cpp \
semaphore_benchmark.cpp \
@@ -47,10 +67,9 @@
LOCAL_MODULE_STEM_32 := bionic-benchmarks32
LOCAL_MODULE_STEM_64 := bionic-benchmarks64
LOCAL_MULTILIB := both
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(benchmark_c_flags)
LOCAL_SRC_FILES := $(benchmark_src_files) property_benchmark.cpp
-LOCAL_CXX_STL := libc++
+LOCAL_STATIC_LIBRARIES += libbenchmark
include $(BUILD_EXECUTABLE)
# We don't build a static benchmark executable because it's not usually
@@ -65,11 +84,10 @@
LOCAL_MODULE_STEM_32 := bionic-benchmarks-glibc32
LOCAL_MODULE_STEM_64 := bionic-benchmarks-glibc64
LOCAL_MULTILIB := both
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_CFLAGS += $(benchmark_c_flags)
LOCAL_LDFLAGS += -lrt
LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_CXX_STL := libc++
+LOCAL_STATIC_LIBRARIES += libbenchmark
include $(BUILD_HOST_EXECUTABLE)
ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/benchmarks/benchmark_main.cpp b/benchmarks/benchmark_main.cpp
index 815d56b..6d83f8a 100644
--- a/benchmarks/benchmark_main.cpp
+++ b/benchmarks/benchmark_main.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "benchmark.h"
+#include <benchmark.h>
#include <regex.h>
#include <stdio.h>
@@ -29,12 +29,15 @@
static int64_t g_bytes_processed;
static int64_t g_benchmark_total_time_ns;
static int64_t g_benchmark_start_time_ns;
-
-typedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap;
-typedef BenchmarkMap::iterator BenchmarkMapIt;
-static BenchmarkMap g_benchmarks;
static int g_name_column_width = 20;
+typedef std::vector<::testing::Benchmark*> BenchmarkList;
+
+static BenchmarkList& Benchmarks() {
+ static BenchmarkList benchmarks;
+ return benchmarks;
+}
+
static int Round(int n) {
int base = 1;
while (base*10 < n) {
@@ -98,7 +101,7 @@
exit(EXIT_FAILURE);
}
- g_benchmarks.insert(std::make_pair(name, this));
+ Benchmarks().push_back(this);
}
void Benchmark::Run() {
@@ -130,19 +133,23 @@
}
void Benchmark::RunWithArg(int arg) {
- // run once in case it's expensive
+ // Run once in case it's expensive.
int iterations = 1;
+ int64_t realStartTime = NanoTime();
RunRepeatedlyWithArg(iterations, arg);
- while (g_benchmark_total_time_ns < 1e9 && iterations < 1e9) {
+ int64_t realTotalTime = NanoTime() - realStartTime;
+ while (realTotalTime < 1e9 && iterations < 1e8) {
int last = iterations;
- if (g_benchmark_total_time_ns/iterations == 0) {
+ if (realTotalTime/iterations == 0) {
iterations = 1e9;
} else {
- iterations = 1e9 / (g_benchmark_total_time_ns/iterations);
+ iterations = 1e9 / (realTotalTime/iterations);
}
iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
iterations = Round(iterations);
+ realStartTime = NanoTime();
RunRepeatedlyWithArg(iterations, arg);
+ realTotalTime = NanoTime() - realStartTime;
}
char throughput[100];
@@ -191,19 +198,18 @@
}
int main(int argc, char* argv[]) {
- if (g_benchmarks.empty()) {
+ if (Benchmarks().empty()) {
fprintf(stderr, "No benchmarks registered!\n");
exit(EXIT_FAILURE);
}
- for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
- int name_width = static_cast<int>(strlen(it->second->Name()));
+ for (auto& b : Benchmarks()) {
+ int name_width = static_cast<int>(strlen(b->Name()));
g_name_column_width = std::max(g_name_column_width, name_width);
}
bool need_header = true;
- for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
- ::testing::Benchmark* b = it->second;
+ for (auto& b : Benchmarks()) {
if (b->ShouldRun(argc, argv)) {
if (need_header) {
printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
@@ -217,8 +223,8 @@
if (need_header) {
fprintf(stderr, "No matching benchmarks!\n");
fprintf(stderr, "Available benchmarks:\n");
- for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
- fprintf(stderr, " %s\n", it->second->Name());
+ for (auto& b : Benchmarks()) {
+ fprintf(stderr, " %s\n", b->Name());
}
exit(EXIT_FAILURE);
}
diff --git a/benchmarks/benchmark.h b/benchmarks/include/benchmark.h
similarity index 95%
rename from benchmarks/benchmark.h
rename to benchmarks/include/benchmark.h
index d7af50f..7e134a0 100644
--- a/benchmarks/benchmark.h
+++ b/benchmarks/include/benchmark.h
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include <stdint.h>
+#ifndef BENCHMARKS_BENCHMARK_H_
+#define BENCHMARKS_BENCHMARK_H_
+#include <stdint.h>
#include <vector>
namespace testing {
@@ -59,3 +61,5 @@
#define BENCHMARK(f) \
static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \
(new ::testing::Benchmark(#f, f))
+
+#endif
diff --git a/libc/arch-arm/include/machine/elf_machdep.h b/libc/arch-arm/include/machine/elf_machdep.h
index 97d8434..542f638 100644
--- a/libc/arch-arm/include/machine/elf_machdep.h
+++ b/libc/arch-arm/include/machine/elf_machdep.h
@@ -102,6 +102,8 @@
/* 112-127 are reserved for private experiments. */
+#define R_ARM_IRELATIVE 160
+
#define R_ARM_RXPC25 249
#define R_ARM_RSBREL32 250
#define R_ARM_THM_RPC22 251
diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index b86e2af..7d3c76e 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -44,12 +44,7 @@
#define PAD_PTR "08" PRIxPTR
#endif
-/* depends how the system includes define this */
-#ifdef HAVE_UNWIND_CONTEXT_STRUCT
typedef struct _Unwind_Context __unwind_context;
-#else
-typedef _Unwind_Context __unwind_context;
-#endif
static mapinfo_t* g_map_info = NULL;
static void* g_demangler;
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 15b3fd5..94b7dd2 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -85,8 +85,10 @@
// because things like environment variables with global scope live on it.
// We also can't free the pthread_internal_t itself, since that lives on the main
// thread's stack rather than on the heap.
+ // The main thread has no mmap allocated space for stack or pthread_internal_t.
+ main_thread.mmap_size = 0;
pthread_attr_init(&main_thread.attr);
- main_thread.attr.flags = PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK | PTHREAD_ATTR_FLAG_MAIN_THREAD;
+ main_thread.attr.flags = PTHREAD_ATTR_FLAG_MAIN_THREAD;
main_thread.attr.guard_size = 0; // The main thread has no guard page.
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.
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 9b45161..e4abee9 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -52,8 +52,9 @@
// This code is used both by each new pthread and the code that initializes the main thread.
void __init_tls(pthread_internal_t* thread) {
- if (thread->user_allocated_stack()) {
- // We don't know where the user got their stack, so assume the worst and zero the TLS area.
+ if (thread->mmap_size == 0) {
+ // If the TLS area was not allocated by mmap(), it may not have been cleared to zero.
+ // So assume the worst and zero the TLS area.
memset(&thread->tls[0], 0, BIONIC_TLS_SLOTS * sizeof(void*));
}
@@ -106,62 +107,63 @@
return error;
}
-static void* __create_thread_stack(size_t stack_size, size_t guard_size) {
+static void* __create_thread_mapped_space(size_t mmap_size, size_t stack_guard_size) {
// Create a new private anonymous map.
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
- void* stack = mmap(NULL, stack_size, prot, flags, -1, 0);
- if (stack == MAP_FAILED) {
+ void* space = mmap(NULL, mmap_size, prot, flags, -1, 0);
+ if (space == MAP_FAILED) {
__libc_format_log(ANDROID_LOG_WARN,
"libc",
- "pthread_create failed: couldn't allocate %zd-byte stack: %s",
- stack_size, strerror(errno));
+ "pthread_create failed: couldn't allocate %zu-bytes mapped space: %s",
+ mmap_size, strerror(errno));
return NULL;
}
- // Set the guard region at the end of the stack to PROT_NONE.
- if (mprotect(stack, guard_size, PROT_NONE) == -1) {
+ // Stack is at the lower end of mapped space, stack guard region is at the lower end of stack.
+ // Set the stack guard region to PROT_NONE, so we can detect thread stack overflow.
+ if (mprotect(space, stack_guard_size, PROT_NONE) == -1) {
__libc_format_log(ANDROID_LOG_WARN, "libc",
- "pthread_create failed: couldn't mprotect PROT_NONE %zd-byte stack guard region: %s",
- guard_size, strerror(errno));
- munmap(stack, stack_size);
+ "pthread_create failed: couldn't mprotect PROT_NONE %zu-byte stack guard region: %s",
+ stack_guard_size, strerror(errno));
+ munmap(space, mmap_size);
return NULL;
}
- return stack;
+ return space;
}
static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, void** child_stack) {
- size_t allocate_stack_size;
+ size_t mmap_size;
uint8_t* stack_top;
if (attr->stack_base == NULL) {
// The caller didn't provide a stack, so allocate one.
// Make sure the stack size and guard size are multiples of PAGE_SIZE.
- allocate_stack_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE);
+ mmap_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE);
attr->guard_size = BIONIC_ALIGN(attr->guard_size, PAGE_SIZE);
- attr->stack_base = __create_thread_stack(allocate_stack_size, attr->guard_size);
+ attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size);
if (attr->stack_base == NULL) {
return EAGAIN;
}
- stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + allocate_stack_size;
+ stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size;
} else {
- // The caller did provide a stack, so remember we're not supposed to free it.
- attr->flags |= PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK;
- allocate_stack_size = 0;
+ // Remember the mmap size is zero and we don't need to free it.
+ mmap_size = 0;
stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + attr->stack_size;
}
- // Thread stack is used for two sections:
- // pthread_internal_t.
- // regular stack, from top to down.
+ // Mapped space(or user allocated stack) is used for:
+ // thread_internal_t (including tls array)
+ // thread stack (including guard page)
stack_top -= sizeof(pthread_internal_t);
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
+ attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(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->allocated_stack_size = allocate_stack_size;
+ thread->mmap_size = mmap_size;
thread->attr = *attr;
__init_tls(thread);
@@ -248,8 +250,8 @@
// be unblocked, but we're about to unmap the memory the mutex is stored in, so this serves as a
// reminder that you can't rewrite this function to use a ScopedPthreadMutexLocker.
pthread_mutex_unlock(&thread->startup_handshake_mutex);
- if (!thread->user_allocated_stack()) {
- munmap(thread->attr.stack_base, thread->allocated_stack_size);
+ if (thread->mmap_size != 0) {
+ munmap(thread->attr.stack_base, thread->mmap_size);
}
__libc_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %s", strerror(errno));
return clone_errno;
diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp
index 715acf1..c800660 100644
--- a/libc/bionic/pthread_detach.cpp
+++ b/libc/bionic/pthread_detach.cpp
@@ -27,29 +27,32 @@
*/
#include <errno.h>
+#include <pthread.h>
#include "pthread_accessor.h"
int pthread_detach(pthread_t t) {
- pthread_accessor thread(t);
- if (thread.get() == NULL) {
+ {
+ pthread_accessor thread(t);
+ if (thread.get() == NULL) {
return ESRCH;
+ }
+
+ if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) {
+ return EINVAL; // Already 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;
+ }
}
- if (thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) {
- return EINVAL; // Already detached.
- }
-
- if (thread->attr.flags & PTHREAD_ATTR_FLAG_JOINED) {
- return 0; // Already being joined; silently do nothing, like glibc.
- }
-
- if (thread->tid == 0) {
- // Already exited; clean up.
- _pthread_internal_remove_locked(thread.get(), true);
- return 0;
- }
-
- thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED;
- return 0;
+ // The thread is in zombie state, 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 ee76e2b..d0d64b0 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -87,30 +87,26 @@
thread->alternate_signal_stack = NULL;
}
- // Keep track of what we need to know about the stack before we lose the pthread_internal_t.
- void* stack_base = thread->attr.stack_base;
- size_t stack_size = thread->allocated_stack_size;
- bool free_stack = false;
-
+ bool free_mapped_space = false;
pthread_mutex_lock(&g_thread_list_lock);
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) {
- // The thread is detached, so we can free the pthread_internal_t.
+ // 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
// because we'll have freed the memory before the thread actually exits.
__set_tid_address(NULL);
// pthread_internal_t is freed below with stack, not here.
_pthread_internal_remove_locked(thread, false);
- if (!thread->user_allocated_stack()) {
- free_stack = true;
- }
+ 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);
- // Detached threads exit with stack teardown, and everything deallocated here.
- // Threads that can be joined exit but leave their stacks for the pthread_join caller to clean up.
- if (free_stack) {
- // We need to munmap the stack we're running on before calling exit.
+ 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.
@@ -119,8 +115,10 @@
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
- _exit_with_stack_teardown(stack_base, stack_size);
+ _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);
}
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 62ec543..8fbaf22 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -35,11 +35,11 @@
/* Has the thread been detached by a pthread_join or pthread_detach call? */
#define PTHREAD_ATTR_FLAG_DETACHED 0x00000001
-/* Was the thread's stack allocated by the user rather than by us? */
-#define PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK 0x00000002
-
/* Has the thread been joined by another thread? */
-#define PTHREAD_ATTR_FLAG_JOINED 0x00000004
+#define PTHREAD_ATTR_FLAG_JOINED 0x00000002
+
+/* Did the thread exit without freeing pthread_internal_t? */
+#define PTHREAD_ATTR_FLAG_ZOMBIE 0x00000004
/* Is this the main thread? */
#define PTHREAD_ATTR_FLAG_MAIN_THREAD 0x80000000
@@ -70,10 +70,6 @@
return (*cached_pid != 0);
}
- bool user_allocated_stack() {
- return (attr.flags & PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK) != 0;
- }
-
pthread_attr_t attr;
__pthread_cleanup_t* cleanup_stack;
@@ -86,8 +82,7 @@
pthread_mutex_t startup_handshake_mutex;
- /* Store real allocated stack size, including thread stack and pthread_internal_t. */
- int allocated_stack_size;
+ size_t mmap_size;
void* tls[BIONIC_TLS_SLOTS];
diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp
index a0a8df0..14061d1 100644
--- a/libc/bionic/pthread_internals.cpp
+++ b/libc/bionic/pthread_internals.cpp
@@ -51,11 +51,9 @@
g_thread_list = thread->next;
}
- // For threads using user allocated stack (including the main thread), the pthread_internal_t
- // can't be freed since it is on the stack.
- if (free_thread && !thread->user_allocated_stack()) {
- // Use one munmap to free allocated stack size, including thread stack and pthread_internal_t.
- munmap(thread->attr.stack_base, thread->allocated_stack_size);
+ 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);
}
}
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index f0d522a..c73c085 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -324,7 +324,11 @@
{
struct addrinfo *next;
- assert(ai != NULL);
+#if __ANDROID__
+ if (ai == NULL) return;
+#else
+ _DIAGASSERT(ai != NULL);
+#endif
do {
next = ai->ai_next;
diff --git a/libc/include/paths.h b/libc/include/paths.h
index a72162f..1eba536 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -33,42 +33,18 @@
#define _PATHS_H_
/* Default search path. */
-#define _PATH_DEFPATH "/usr/bin:/bin"
-/* All standard utilities path. */
-#define _PATH_STDPATH \
- "/usr/bin:/bin:/usr/sbin:/sbin"
+#define _PATH_DEFPATH "/system/bin:/system/xbin"
#define _PATH_BSHELL "/system/bin/sh"
#define _PATH_CONSOLE "/dev/console"
-#define _PATH_CSHELL "/bin/csh"
-#define _PATH_DEVDB "/var/run/dev.db"
#define _PATH_DEVNULL "/dev/null"
-#define _PATH_DRUM "/dev/drum"
#define _PATH_KLOG "/proc/kmsg"
-#define _PATH_KMEM "/dev/kmem"
-#define _PATH_LASTLOG "/var/log/lastlog"
-#define _PATH_MAILDIR "/var/mail"
-#define _PATH_MAN "/usr/share/man"
#define _PATH_MEM "/dev/mem"
-#define _PATH_MNTTAB "/etc/fstab"
-#define _PATH_MOUNTED "/etc/mtab"
-#define _PATH_NOLOGIN "/etc/nologin"
-#define _PATH_PRESERVE "/var/lib"
-#define _PATH_RWHODIR "/var/spool/rwho"
-#define _PATH_SENDMAIL "/usr/sbin/sendmail"
-#define _PATH_SHADOW "/etc/shadow"
-#define _PATH_SHELLS "/etc/shells"
+
+#define _PATH_MOUNTED "/proc/mounts"
#define _PATH_TTY "/dev/tty"
-#define _PATH_UNIX "/boot/vmlinux"
-#define _PATH_UTMP "/var/run/utmp"
-#define _PATH_VI "/bin/vi"
-#define _PATH_WTMP "/var/log/wtmp"
/* Provide trailing slash, since mostly used for building pathnames. */
#define _PATH_DEV "/dev/"
-#define _PATH_TMP "/tmp/"
-#define _PATH_VARDB "/var/db/"
-#define _PATH_VARRUN "/var/run/"
-#define _PATH_VARTMP "/var/tmp/"
#endif /* !_PATHS_H_ */
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index 0349e4c..44fe991 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -50,7 +50,7 @@
__BEGIN_DECLS
-struct prop_msg
+struct prop_msg
{
unsigned cmd;
char name[PROP_NAME_MAX];
@@ -58,7 +58,7 @@
};
#define PROP_MSG_SETPROP 1
-
+
/*
** Rules:
**
@@ -82,6 +82,7 @@
#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"
#define PROP_PATH_FACTORY "/factory/factory.prop"
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index dd2a0bb..b68d704 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -180,6 +180,25 @@
} fp_r;
} fpregset_t;
+#ifdef __LP64__
+typedef struct {
+ gregset_t gregs;
+ fpregset_t fpregs;
+ greg_t mdhi;
+ greg_t hi1;
+ greg_t hi2;
+ greg_t hi3;
+ greg_t mdlo;
+ greg_t lo1;
+ greg_t lo2;
+ greg_t lo3;
+ greg_t pc;
+ uint32_t fpc_csr;
+ uint32_t used_math;
+ uint32_t dsp;
+ uint32_t reserved;
+} mcontext_t;
+#else
typedef struct {
unsigned regmask;
unsigned status;
@@ -200,6 +219,7 @@
unsigned long hi3;
unsigned long lo3;
} mcontext_t;
+#endif
typedef struct ucontext {
unsigned long uc_flags;
@@ -209,10 +229,6 @@
sigset_t uc_sigmask;
} ucontext_t;
-#elif defined(__mips64__)
-
-#error TODO
-
#elif defined(__x86_64__)
enum {
diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h
index 8783467..8f55a26 100644
--- a/libc/upstream-openbsd/android/include/openbsd-compat.h
+++ b/libc/upstream-openbsd/android/include/openbsd-compat.h
@@ -59,6 +59,11 @@
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+/* OpenBSD has this in paths.h. But this directory doesn't normally exist.
+ * Even when it does exist, only the 'shell' user has permissions.
+ */
+#define _PATH_TMP "/data/local/tmp/"
+
/* We have OpenBSD's getentropy_linux.c, but we don't mention getentropy in any header. */
__LIBC_HIDDEN__ extern int getentropy(void*, size_t);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fgetln.c b/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
index d0c0809..1109cf2 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fgetln.c,v 1.12 2013/11/12 07:04:06 deraadt Exp $ */
+/* $OpenBSD: fgetln.c,v 1.13 2015/01/05 21:58:52 millert Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -38,19 +38,12 @@
/*
* Expand the line buffer. Return -1 on error.
-#ifdef notdef
- * The `new size' does not account for a terminating '\0',
- * so we add 1 here.
-#endif
*/
static int
__slbexpand(FILE *fp, size_t newsize)
{
void *p;
-#ifdef notdef
- ++newsize;
-#endif
if (fp->_lb._size >= newsize)
return (0);
if ((p = realloc(fp->_lb._base, newsize)) == NULL)
@@ -141,14 +134,11 @@
}
*lenp = len;
ret = (char *)fp->_lb._base;
-#ifdef notdef
- ret[len] = '\0';
-#endif
FUNLOCKFILE(fp);
return (ret);
error:
- *lenp = 0; /* ??? */
FUNLOCKFILE(fp);
- return (NULL); /* ??? */
+ *lenp = 0;
+ return (NULL);
}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/getdelim.c b/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
index dcde0c3..5e583cb 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: getdelim.c,v 1.1 2012/03/21 23:44:35 fgsch Exp $ */
+/* $OpenBSD: getdelim.c,v 1.2 2014/10/16 17:31:51 millert Exp $ */
/* $NetBSD: getdelim.c,v 1.13 2011/07/22 23:12:30 joerg Exp $ */
/*
@@ -78,13 +78,12 @@
else
len = (p - fp->_p) + 1;
- newlen = off + len;
/* Ensure we can handle it */
- if (newlen < off || newlen > SSIZE_MAX) {
+ if (off > SSIZE_MAX || len + 1 > SSIZE_MAX - off) {
errno = EOVERFLOW;
goto error;
}
- newlen++; /* reserve space for the NULL terminator */
+ newlen = off + len + 1; /* reserve space for NUL terminator */
if (newlen > *buflen) {
if (newlen < MINBUF)
newlen = MINBUF;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/makebuf.c b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
index d47e27c..56e5f21 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: makebuf.c,v 1.8 2005/12/28 18:50:22 millert Exp $ */
+/* $OpenBSD: makebuf.c,v 1.9 2015/01/13 07:18:21 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -65,7 +65,6 @@
fp->_bf._size = 1;
return;
}
- __atexit_register_cleanup(_cleanup);
flags |= __SMBF;
fp->_bf._base = fp->_p = p;
fp->_bf._size = size;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/mktemp.c b/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
index 2a17e52..956608c 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mktemp.c,v 1.34 2014/08/31 02:21:18 guenther Exp $ */
+/* $OpenBSD: mktemp.c,v 1.35 2014/10/31 15:54:14 millert Exp $ */
/*
* Copyright (c) 1996-1998, 2008 Theo de Raadt
* Copyright (c) 1997, 2008-2009 Todd C. Miller
@@ -45,7 +45,7 @@
mktemp_internal(char *path, int slen, int mode, int flags)
{
char *start, *cp, *ep;
- const char *tempchars = TEMPCHARS;
+ const char tempchars[] = TEMPCHARS;
unsigned int tries;
struct stat sb;
size_t len;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c b/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
index 9414187..391a944 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: open_wmemstream.c,v 1.3 2014/03/06 07:28:21 gerhard Exp $ */
+/* $OpenBSD: open_wmemstream.c,v 1.4 2014/10/08 05:28:19 deraadt Exp $ */
/*
* Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
@@ -52,7 +52,7 @@
if (sz < end + 1)
sz = end + 1;
- p = realloc(st->string, sz * sizeof(wchar_t));
+ p = reallocarray(st->string, sz, sizeof(wchar_t));
if (!p)
return (-1);
bzero(p + st->size, (sz - st->size) * sizeof(wchar_t));
diff --git a/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
index 6c49f7a..9b2ab57 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: setvbuf.c,v 1.11 2009/11/09 00:18:27 kurt Exp $ */
+/* $OpenBSD: setvbuf.c,v 1.12 2015/01/13 07:18:21 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -115,6 +115,13 @@
}
/*
+ * We're committed to buffering from here, so make sure we've
+ * registered to flush buffers on exit.
+ */
+ if (!__sdidinit)
+ __sinit();
+
+ /*
* Kill any seek optimization if the buffer is not the
* right size.
*
@@ -124,8 +131,7 @@
flags |= __SNPT;
/*
- * Fix up the FILE fields, and set __cleanup for output flush on
- * exit (since we are buffered in some way).
+ * Fix up the FILE fields.
*/
if (mode == _IOLBF)
flags |= __SLBF;
@@ -148,7 +154,6 @@
fp->_w = 0;
}
FUNLOCKFILE(fp);
- __atexit_register_cleanup(_cleanup);
return (ret);
}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/ungetc.c b/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
index 675733a..ec98f26 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ungetc.c,v 1.12 2009/11/09 00:18:27 kurt Exp $ */
+/* $OpenBSD: ungetc.c,v 1.13 2014/10/11 04:05:10 deraadt Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -64,14 +64,14 @@
return (0);
}
i = _UB(fp)._size;
- p = realloc(_UB(fp)._base, i << 1);
+ p = reallocarray(_UB(fp)._base, i, 2);
if (p == NULL)
return (EOF);
/* no overlap (hence can use memcpy) because we doubled the size */
(void)memcpy((void *)(p + i), (void *)p, (size_t)i);
fp->_p = p + i;
_UB(fp)._base = p;
- _UB(fp)._size = i << 1;
+ _UB(fp)._size = i * 2;
return (0);
}
diff --git a/linker/Android.mk b/linker/Android.mk
index 0383e7b..720389f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -16,15 +16,8 @@
LOCAL_SRC_FILES_arm64 := arch/arm64/begin.S
LOCAL_SRC_FILES_x86 := arch/x86/begin.c
LOCAL_SRC_FILES_x86_64 := arch/x86_64/begin.S
-LOCAL_SRC_FILES_mips := arch/mips/begin.S
-LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S
-
-# GNU assembler aborted with clang's output for linker.cpp:
-# Assertion failure in get_line_subseg at
-# /s/ndk-toolchain/src/build/../binutils/binutils-2.24/gas/dwarf2dbg.c line 271.
-ifeq ($(TARGET_ARCH),mips)
- LOCAL_CLANG_CFLAGS += -integrated-as
-endif
+LOCAL_SRC_FILES_mips := arch/mips/begin.S linker_mips.cpp
+LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S linker_mips.cpp
LOCAL_LDFLAGS := \
-shared \
diff --git a/linker/linker.cpp b/linker/linker.cpp
index babefeb..0b0afc3 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -36,6 +36,7 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
+#include <sys/personality.h>
#include <unistd.h>
#include <new>
@@ -52,6 +53,7 @@
#include "linker_debug.h"
#include "linker_environ.h"
#include "linker_phdr.h"
+#include "linker_relocs.h"
#include "linker_allocator.h"
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
@@ -120,14 +122,6 @@
__LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
-enum RelocationKind {
- kRelocAbsolute = 0,
- kRelocRelative,
- kRelocCopy,
- kRelocSymbol,
- kRelocMax
-};
-
#if STATS
struct linker_stats_t {
int count[kRelocMax];
@@ -135,30 +129,16 @@
static linker_stats_t linker_stats;
-static void count_relocation(RelocationKind kind) {
+void count_relocation(RelocationKind kind) {
++linker_stats.count[kind];
}
#else
-static void count_relocation(RelocationKind) {
+void count_relocation(RelocationKind) {
}
#endif
#if COUNT_PAGES
-static unsigned bitmask[4096];
-#if defined(__LP64__)
-#define MARK(offset) \
- do { \
- if ((((offset) >> 12) >> 5) < 4096) \
- bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
- } while (0)
-#else
-#define MARK(offset) \
- do { \
- bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
- } while (0)
-#endif
-#else
-#define MARK(x) do {} while (0)
+uint32_t bitmask[4096];
#endif
// You shouldn't try to call memory-allocating functions in the dynamic linker.
@@ -537,7 +517,7 @@
return gnu_hash_;
}
-static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
+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) {
SymbolName symbol_name(name);
ElfW(Sym)* s = nullptr;
@@ -1277,17 +1257,33 @@
return ifunc_addr;
}
+#if !defined(__mips__)
#if defined(USE_RELA)
-int soinfo::relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rela) {
- unsigned type = ELFW(R_TYPE)(rela->r_info);
- unsigned sym = ELFW(R_SYM)(rela->r_info);
- ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rela->r_offset + load_bias);
+static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
+ return rela->r_addend;
+}
+#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) {
+ return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
+ }
+ return 0;
+}
+#endif
+
+template<typename ElfRelT>
+bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ for (size_t idx = 0; idx < count; ++idx, ++rel) {
+ ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
+ ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
+
+ ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
ElfW(Addr) sym_addr = 0;
const char* sym_name = nullptr;
+ ElfW(Addr) addend = get_addend(rel, reloc);
- DEBUG("Processing '%s' relocation at index %zd", name, idx);
- if (type == 0) { // R_*_NONE
+ DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
+ if (type == R_GENERIC_NONE) {
continue;
}
@@ -1302,7 +1298,7 @@
s = &symtab_[sym];
if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
- return -1;
+ return false;
}
/* IHI0044C AAELF 4.5.1.1:
@@ -1318,36 +1314,40 @@
*/
switch (type) {
+ case R_GENERIC_JUMP_SLOT:
+ case R_GENERIC_GLOB_DAT:
+ case R_GENERIC_RELATIVE:
+ case R_GENERIC_IRELATIVE:
#if defined(__aarch64__)
- case R_AARCH64_JUMP_SLOT:
- case R_AARCH64_GLOB_DAT:
case R_AARCH64_ABS64:
case R_AARCH64_ABS32:
case R_AARCH64_ABS16:
- case R_AARCH64_RELATIVE:
- case R_AARCH64_IRELATIVE:
+#elif defined(__x86_64__)
+ case R_X86_64_32:
+ case R_X86_64_64:
+#elif defined(__arm__)
+ case R_ARM_ABS32:
+#elif defined(__i386__)
+ case R_386_32:
+#endif
/*
* The sym_addr was initialized to be zero above, or the relocation
* code below does not care about value of sym_addr.
* No need to do anything.
*/
break;
-#elif defined(__x86_64__)
- case R_X86_64_JUMP_SLOT:
- case R_X86_64_GLOB_DAT:
- case R_X86_64_32:
- case R_X86_64_64:
- case R_X86_64_RELATIVE:
- case R_X86_64_IRELATIVE:
- // No need to do anything.
- break;
+#if defined(__x86_64__)
case R_X86_64_PC32:
sym_addr = reloc;
break;
+#elif defined(__i386__)
+ case R_386_PC32:
+ sym_addr = reloc;
+ break;
#endif
default:
- DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rela, idx);
- return -1;
+ DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
+ return false;
}
} else {
// We got a definition.
@@ -1357,119 +1357,120 @@
}
switch (type) {
+ case R_GENERIC_JUMP_SLOT:
+ count_relocation(kRelocAbsolute);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO JMP_SLOT %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
+ break;
+ case R_GENERIC_GLOB_DAT:
+ count_relocation(kRelocAbsolute);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO GLOB_DAT %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
+ break;
+ case R_GENERIC_RELATIVE:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(base + addend));
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (base + addend);
+ break;
+ case R_GENERIC_IRELATIVE:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(base + addend));
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + addend);
+ break;
+
#if defined(__aarch64__)
- case R_AARCH64_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + rela->r_addend);
- break;
- case R_AARCH64_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + rela->r_addend);
- break;
case R_AARCH64_ABS64:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS64 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
break;
case R_AARCH64_ABS32:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS32 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
static_cast<ElfW(Addr)>(INT32_MIN),
static_cast<ElfW(Addr)>(UINT32_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_ABS16:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS16 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
static_cast<ElfW(Addr)>(INT16_MIN),
static_cast<ElfW(Addr)>(UINT16_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_PREL64:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL64 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend) - rela->r_offset;
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend) - rel->r_offset;
break;
case R_AARCH64_PREL32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL32 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
static_cast<ElfW(Addr)>(INT32_MIN),
static_cast<ElfW(Addr)>(UINT32_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_PREL16:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL16 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
static_cast<ElfW(Addr)>(INT16_MIN),
static_cast<ElfW(Addr)>(UINT16_MAX));
- return -1;
+ return false;
}
break;
- case R_AARCH64_RELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %16llx <- %16llx\n",
- reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (base + rela->r_addend);
- break;
-
- case R_AARCH64_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %16llx <- %16llx\n", reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + rela->r_addend);
- break;
-
case R_AARCH64_COPY:
/*
* ET_EXEC is not supported so this should not happen.
@@ -1481,175 +1482,39 @@
* set to ET_EXEC.
*/
DL_ERR("%s R_AARCH64_COPY relocations are not supported", name);
- return -1;
+ return false;
case R_AARCH64_TLS_TPREL64:
TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset);
break;
case R_AARCH64_TLS_DTPREL32:
TRACE_TYPE(RELO, "RELO TLS_DTPREL32 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset);
break;
#elif defined(__x86_64__)
- case R_X86_64_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
- break;
- case R_X86_64_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
- break;
- case R_X86_64_RELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %08zx <- +%08zx", static_cast<size_t>(reloc),
- static_cast<size_t>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = base + rela->r_addend;
- break;
- case R_X86_64_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %16llx <- %16llx\n", reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + rela->r_addend);
- break;
case R_X86_64_32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
static_cast<size_t>(sym_addr), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;
case R_X86_64_64:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_64 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
static_cast<size_t>(sym_addr), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;
case R_X86_64_PC32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend - reloc;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - reloc;
break;
-#endif
-
- default:
- DL_ERR("unknown reloc type %d @ %p (%zu)", type, rela, idx);
- return -1;
- }
- }
- return 0;
-}
-
-#else // REL, not RELA.
-int soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rel) {
- unsigned type = ELFW(R_TYPE)(rel->r_info);
- // TODO: don't use unsigned for 'sym'. Use uint32_t or ElfW(Addr) instead.
- unsigned sym = ELFW(R_SYM)(rel->r_info);
- ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
- ElfW(Addr) sym_addr = 0;
- const char* sym_name = nullptr;
-
- DEBUG("Processing '%s' relocation at index %zd", name, idx);
- if (type == 0) { // R_*_NONE
- continue;
- }
-
- 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);
- if (s == nullptr) {
- // 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);
- return -1;
- }
-
- /* IHI0044C AAELF 4.5.1.1:
-
- Libraries are not searched to resolve weak references.
- It is not an error for a weak reference to remain
- unsatisfied.
-
- During linking, the value of an undefined weak reference is:
- - Zero if the relocation type is absolute
- - The address of the place if the relocation is pc-relative
- - The address of nominal base address if the relocation
- type is base-relative.
- */
-
- switch (type) {
-#if defined(__arm__)
- case R_ARM_JUMP_SLOT:
- case R_ARM_GLOB_DAT:
- case R_ARM_ABS32:
- case R_ARM_RELATIVE: /* Don't care. */
- // sym_addr was initialized to be zero above or relocation
- // code below does not care about value of sym_addr.
- // No need to do anything.
- break;
-#elif defined(__i386__)
- case R_386_JMP_SLOT:
- case R_386_GLOB_DAT:
- case R_386_32:
- case R_386_RELATIVE: /* Don't care. */
- case R_386_IRELATIVE:
- // sym_addr was initialized to be zero above or relocation
- // code below does not care about value of sym_addr.
- // No need to do anything.
- break;
- case R_386_PC32:
- sym_addr = reloc;
- break;
-#endif
-
-#if defined(__arm__)
- case R_ARM_COPY:
- // Fall through. Can't really copy if weak symbol is not found at run-time.
-#endif
- default:
- DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
- return -1;
- }
- } else {
- // We got a definition.
- sym_addr = lsi->resolve_symbol_address(s);
- }
- count_relocation(kRelocSymbol);
- }
-
- switch (type) {
-#if defined(__arm__)
- case R_ARM_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
- case R_ARM_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
+#elif defined(__arm__)
case R_ARM_ABS32:
count_relocation(kRelocAbsolute);
MARK(rel->r_offset);
@@ -1674,20 +1539,8 @@
* set to ET_EXEC.
*/
DL_ERR("%s R_ARM_COPY relocations are not supported", name);
- return -1;
+ return false;
#elif defined(__i386__)
- case R_386_JMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
- case R_386_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
case R_386_32:
count_relocation(kRelocRelative);
MARK(rel->r_offset);
@@ -1701,113 +1554,15 @@
reloc, (sym_addr - reloc), sym_addr, reloc, sym_name);
*reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr - reloc);
break;
-#elif defined(__mips__)
- case R_MIPS_REL32:
-#if defined(__LP64__)
- // MIPS Elf64_Rel entries contain compound relocations
- // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
- if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
- ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
- DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
- type, (unsigned)ELF64_R_TYPE2(rel->r_info),
- (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
- return -1;
- }
#endif
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
- if (s) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
- } else {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
- }
- break;
-#endif
-
-#if defined(__arm__)
- case R_ARM_RELATIVE:
-#elif defined(__i386__)
- case R_386_RELATIVE:
-#endif
- count_relocation(kRelocRelative);
- MARK(rel->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %p <- +%p",
- reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
- break;
-#if defined(__i386__)
- case R_386_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %p <- %p", reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + *reinterpret_cast<ElfW(Addr)*>(reloc));
- break;
-#endif
-
default:
DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
- return -1;
- }
- }
- return 0;
-}
-#endif
-
-#if defined(__mips__)
-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;
- }
-
- // got[0] is the address of the lazy resolver function.
- // got[1] may be used for a GNU extension.
- // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
- // FIXME: maybe this should be in a separate routine?
- if ((flags_ & FLAG_LINKER) == 0) {
- size_t g = 0;
- got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
- if (reinterpret_cast<intptr_t>(got[g]) < 0) {
- got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
- }
- // Relocate the local GOT entries.
- for (; g < mips_local_gotno_; g++) {
- got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
- }
- }
-
- // Now for the global GOT entries...
- ElfW(Sym)* sym = symtab_ + mips_gotsym_;
- got = plt_got_ + mips_local_gotno_;
- for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
- // 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);
- if (s == nullptr) {
- // We only allow an undefined symbol if this is a weak reference.
- s = &symtab_[g];
- if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
- DL_ERR("cannot locate \"%s\"...", sym_name);
return false;
- }
- *got = 0;
- } else {
- // FIXME: is this sufficient?
- // For reference see NetBSD link loader
- // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
- *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
}
}
return true;
}
-#endif
+#endif // !defined(__mips__)
void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
if (functions == nullptr) {
@@ -2496,26 +2251,26 @@
#if defined(USE_RELA)
if (rela_ != nullptr) {
DEBUG("[ relocating %s ]", name);
- if (relocate(rela_, rela_count_, global_group, local_group)) {
+ if (!relocate(rela_, rela_count_, global_group, local_group)) {
return false;
}
}
if (plt_rela_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
+ if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
return false;
}
}
#else
if (rel_ != nullptr) {
DEBUG("[ relocating %s ]", name);
- if (relocate(rel_, rel_count_, global_group, local_group)) {
+ if (!relocate(rel_, rel_count_, global_group, local_group)) {
return false;
}
}
if (plt_rel_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
+ if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
return false;
}
}
@@ -2663,6 +2418,12 @@
ldpreload_env = linker_env_get("LD_PRELOAD");
}
+#if !defined(__LP64__)
+ if (personality(PER_LINUX32) == -1) {
+ __libc_fatal("error setting PER_LINUX32 personality: %s", strerror(errno));
+ }
+#endif
+
INFO("[ android linker & debugger ]");
soinfo* si = soinfo_alloc(args.argv[0], nullptr, 0, RTLD_GLOBAL);
@@ -2717,7 +2478,10 @@
somain = si;
- si->prelink_image();
+ if (!si->prelink_image()) {
+ __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
+ exit(EXIT_FAILURE);
+ }
// add somain to global group
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
diff --git a/linker/linker.h b/linker/linker.h
index f7aa11c..2afbaf6 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -286,11 +286,8 @@
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);
-#if defined(USE_RELA)
- int relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
-#else
- int relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
-#endif
+ template<typename ElfRelT>
+ bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
private:
// This part of the structure is only available
@@ -321,6 +318,19 @@
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);
+
+enum RelocationKind {
+ kRelocAbsolute = 0,
+ kRelocRelative,
+ kRelocCopy,
+ kRelocSymbol,
+ kRelocMax
+};
+
+void count_relocation(RelocationKind kind);
+
soinfo* get_libdl_info();
void do_android_get_LD_LIBRARY_PATH(char*, size_t);
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 0c7a784..5ded5ab 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -82,4 +82,23 @@
#define TRACE_TYPE(t, x...) do { if (DO_TRACE_##t) { TRACE(x); } } while (0)
+#if COUNT_PAGES
+extern uint32_t bitmask[];
+#if defined(__LP64__)
+#define MARK(offset) \
+ do { \
+ if ((((offset) >> 12) >> 5) < 4096) \
+ bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
+ } while (0)
+#else
+#define MARK(offset) \
+ do { \
+ bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
+ } while (0)
+#endif
+#else
+#define MARK(x) do {} while (0)
+
+#endif
+
#endif /* _LINKER_DEBUG_H_ */
diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp
new file mode 100644
index 0000000..eb2ae84
--- /dev/null
+++ b/linker/linker_mips.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "linker.h"
+#include "linker_debug.h"
+#include "linker_relocs.h"
+
+template<>
+bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ for (size_t idx = 0; idx < count; ++idx, ++rel) {
+ ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
+ ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
+
+ ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
+ ElfW(Addr) sym_addr = 0;
+ const char* sym_name = nullptr;
+
+ DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
+ if (type == R_GENERIC_NONE) {
+ continue;
+ }
+
+ 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);
+ if (s == nullptr) {
+ // mips does not support relocation with weak-undefined symbols
+ DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
+ return false;
+ } else {
+ // We got a definition.
+ sym_addr = lsi->resolve_symbol_address(s);
+ }
+ count_relocation(kRelocSymbol);
+ }
+
+ switch (type) {
+ case R_MIPS_REL32:
+#if defined(__LP64__)
+ // MIPS Elf64_Rel entries contain compound relocations
+ // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
+ if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
+ ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
+ DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
+ type, (unsigned)ELF64_R_TYPE2(rel->r_info),
+ (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
+ return false;
+ }
+#endif
+ count_relocation(s == nullptr ? kRelocAbsolute : kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
+ static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
+ if (s != nullptr) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
+ } else {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
+ }
+ break;
+ default:
+ DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
+ return false;
+ }
+ }
+ return true;
+}
+
+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;
+ }
+
+ // got[0] is the address of the lazy resolver function.
+ // got[1] may be used for a GNU extension.
+ // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
+ // FIXME: maybe this should be in a separate routine?
+ if ((flags_ & FLAG_LINKER) == 0) {
+ size_t g = 0;
+ got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
+ if (reinterpret_cast<intptr_t>(got[g]) < 0) {
+ got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
+ }
+ // Relocate the local GOT entries.
+ for (; g < mips_local_gotno_; g++) {
+ got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
+ }
+ }
+
+ // Now for the global GOT entries...
+ ElfW(Sym)* sym = symtab_ + mips_gotsym_;
+ got = plt_got_ + mips_local_gotno_;
+ for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
+ // 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);
+ if (s == nullptr) {
+ // We only allow an undefined symbol if this is a weak reference.
+ s = &symtab_[g];
+ if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
+ DL_ERR("cannot locate \"%s\"...", sym_name);
+ return false;
+ }
+ *got = 0;
+ } else {
+ // FIXME: is this sufficient?
+ // For reference see NetBSD link loader
+ // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
+ *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
+ }
+ }
+ return true;
+}
+
diff --git a/linker/linker_relocs.h b/linker/linker_relocs.h
new file mode 100644
index 0000000..12c1497
--- /dev/null
+++ b/linker/linker_relocs.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef __LINKER_RELOCS_H
+#define __LINKER_RELOCS_H
+
+#include <elf.h>
+
+#define R_GENERIC_NONE 0 // R_*_NONE is always 0
+
+#if defined (__aarch64__)
+
+#define R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT
+#define R_GENERIC_RELATIVE R_AARCH64_RELATIVE
+#define R_GENERIC_IRELATIVE R_AARCH64_IRELATIVE
+
+#elif defined (__arm__)
+
+#define R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT
+#define R_GENERIC_RELATIVE R_ARM_RELATIVE
+#define R_GENERIC_IRELATIVE R_ARM_IRELATIVE
+
+#elif defined (__i386__)
+
+#define R_GENERIC_JUMP_SLOT R_386_JMP_SLOT
+#define R_GENERIC_GLOB_DAT R_386_GLOB_DAT
+#define R_GENERIC_RELATIVE R_386_RELATIVE
+#define R_GENERIC_IRELATIVE R_386_IRELATIVE
+
+#elif defined (__x86_64__)
+
+#define R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT
+#define R_GENERIC_RELATIVE R_X86_64_RELATIVE
+#define R_GENERIC_IRELATIVE R_X86_64_IRELATIVE
+
+#endif
+
+#endif // __LINKER_RELOCS_H
diff --git a/tests/Android.mk b/tests/Android.mk
index dfb89d2..38d85f8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -93,6 +93,7 @@
sstream_test.cpp \
sys_epoll_test.cpp \
sys_mman_test.cpp \
+ sys_personality_test.cpp \
sys_resource_test.cpp \
sys_select_test.cpp \
sys_sendfile_test.cpp \
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 4304d8c..6c5023b 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -42,8 +42,8 @@
void ColoredPrintf(GTestColor color, const char* fmt, ...);
-} // namespace internal
-} // namespace testing
+} // namespace internal
+} // namespace testing
using testing::internal::GTestColor;
using testing::internal::COLOR_DEFAULT;
@@ -73,6 +73,24 @@
return global_test_run_warnline_in_ms;
}
+static void PrintHelpInfo() {
+ printf("Bionic Unit Test Options:\n"
+ " -j [JOB_COUNT]\n"
+ " Run up to JOB_COUNT tests in parallel.\n"
+ " Use isolation mode, Run each test in a separate process.\n"
+ " If JOB_COUNT is not given, it is set to the count of available processors.\n"
+ " --no-isolate\n"
+ " Don't use isolation mode, run all tests in a single process.\n"
+ " --deadline=[TIME_IN_MS]\n"
+ " Run each test in no longer than [TIME_IN_MS] time.\n"
+ " It takes effect only in isolation mode. Deafult deadline is 60000 ms.\n"
+ " --warnline=[TIME_IN_MS]\n"
+ " Test running longer than [TIME_IN_MS] will be warned.\n"
+ " It takes effect only in isolation mode. Default warnline is 2000 ms.\n"
+ "\nDefault bionic unit test option is -j.\n"
+ "\n");
+}
+
enum TestResult {
TEST_SUCCESS = 0,
TEST_FAILED,
@@ -90,37 +108,37 @@
test_list_.push_back(std::make_tuple(test_name, TEST_FAILED, 0LL));
}
- int NumTests() const { return test_list_.size(); }
+ size_t TestCount() const { return test_list_.size(); }
- std::string GetTestName(int test_id) const {
+ std::string GetTestName(size_t test_id) const {
VerifyTestId(test_id);
return name_ + "." + std::get<0>(test_list_[test_id]);
}
- void SetTestResult(int test_id, TestResult result) {
+ void SetTestResult(size_t test_id, TestResult result) {
VerifyTestId(test_id);
std::get<1>(test_list_[test_id]) = result;
}
- TestResult GetTestResult(int test_id) const {
+ TestResult GetTestResult(size_t test_id) const {
VerifyTestId(test_id);
return std::get<1>(test_list_[test_id]);
}
- void SetTestTime(int test_id, int64_t elapsed_time) {
+ void SetTestTime(size_t test_id, int64_t elapsed_time) {
VerifyTestId(test_id);
std::get<2>(test_list_[test_id]) = elapsed_time;
}
- int64_t GetTestTime(int test_id) const {
+ int64_t GetTestTime(size_t test_id) const {
VerifyTestId(test_id);
return std::get<2>(test_list_[test_id]);
}
private:
- void VerifyTestId(int test_id) const {
- if(test_id < 0 || test_id >= static_cast<int>(test_list_.size())) {
- fprintf(stderr, "test_id %d out of range [0, %zu)\n", test_id, test_list_.size());
+ void VerifyTestId(size_t test_id) const {
+ if(test_id >= test_list_.size()) {
+ fprintf(stderr, "test_id %zu out of range [0, %zu)\n", test_id, test_list_.size());
exit(1);
}
}
@@ -167,15 +185,15 @@
int towrite = strlen(buf);
char* p = buf;
while (towrite > 0) {
- ssize_t num_write = write(fileno(stdout), p, towrite);
- if (num_write == -1) {
+ ssize_t write_count = write(fileno(stdout), p, towrite);
+ if (write_count == -1) {
if (errno != EINTR) {
fprintf(stderr, "write, errno = %d\n", errno);
break;
}
} else {
- towrite -= num_write;
- p += num_write;
+ towrite -= write_count;
+ p += write_count;
}
}
}
@@ -265,46 +283,36 @@
return (result != -1 && WEXITSTATUS(result) == 0);
}
-static void PrintHelpInfo() {
- printf("Bionic Unit Test Options:\n"
- " --isolate\n"
- " Use isolation mode, Run each test in a separate process.\n"
- " --deadline=[TIME_IN_MS]\n"
- " Run each test in no longer than [TIME_IN_MS] time.\n"
- " It takes effect only in isolation mode. Deafult deadline is 60000 ms.\n"
- " --warnline=[TIME_IN_MS]\n"
- " Test running longer than [TIME_IN_MS] will be warned.\n"
- " It takes effect only in isolation mode. Default warnline is 2000 ms.\n"
- " -j [JOB_NUM]\n"
- " Run up to JOB_NUM tests in parallel.\n"
- " Use isolation mode, Run each test in a separate process.\n"
- " If JOB_NUM is not given, it is set to the number of cpus available.\n"
- "\n");
-}
-
// Part of the following *Print functions are copied from external/gtest/src/gtest.cc:
// 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>& testcase_list, int iteration,
- int num_iterations) {
- if (num_iterations > 1) {
- printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration);
+static void OnTestIterationStartPrint(const std::vector<TestCase>& testcase_list, size_t iteration,
+ size_t iteration_count) {
+ if (iteration_count > 1) {
+ printf("\nRepeating all tests (iteration %zu) . . .\n\n", iteration);
}
ColoredPrintf(COLOR_GREEN, "[==========] ");
- int num_testcases = testcase_list.size();
- int num_tests = 0;
+ size_t testcase_count = testcase_list.size();
+ size_t test_count = 0;
for (const auto& testcase : testcase_list) {
- num_tests += testcase.NumTests();
+ test_count += testcase.TestCount();
}
- printf("Running %d %s from %d %s.\n",
- num_tests, (num_tests == 1) ? "test" : "tests",
- num_testcases, (num_testcases == 1) ? "test case" : "test cases");
+ printf("Running %zu %s from %zu %s.\n",
+ test_count, (test_count == 1) ? "test" : "tests",
+ testcase_count, (testcase_count == 1) ? "test case" : "test cases");
fflush(stdout);
}
-static void OnTestTimeoutPrint(const TestCase& testcase, int test_id) {
+static void OnTestTerminatedPrint(const TestCase& testcase, size_t test_id, int sig) {
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s terminated by signal: %s\n", testcase.GetTestName(test_id).c_str(),
+ strsignal(sig));
+ fflush(stdout);
+}
+
+static void OnTestTimeoutPrint(const TestCase& testcase, size_t test_id) {
ColoredPrintf(COLOR_RED, "[ TIMEOUT ] ");
printf("%s (killed by timeout at %lld ms)\n", testcase.GetTestName(test_id).c_str(),
testcase.GetTestTime(test_id) / 1000000LL);
@@ -313,17 +321,17 @@
static void TestcaseTimePrint(const TestCase& testcase) {
int64_t testcase_time = 0;
- for (int i = 0; i < testcase.NumTests(); ++i) {
+ for (size_t i = 0; i < testcase.TestCount(); ++i) {
testcase_time += testcase.GetTestTime(i);
}
- printf("%d %s from %s (%lld ms total)\n", testcase.NumTests(),
- (testcase.NumTests() == 1) ? "test" : "tests",
- testcase.GetName().c_str(),
- testcase_time / 1000000LL);
+ printf("%zu %s from %s (%lld ms total)\n", testcase.TestCount(),
+ (testcase.TestCount() == 1) ? "test" : "tests",
+ testcase.GetName().c_str(),
+ testcase_time / 1000000LL);
fflush(stdout);
}
-static void OnTestIterationEndPrint(const std::vector<TestCase>& testcase_list, int /*iteration*/,
+static void OnTestIterationEndPrint(const std::vector<TestCase>& testcase_list, size_t /*iteration*/,
int64_t elapsed_time) {
std::vector<std::string> fail_test_name_list;
@@ -331,16 +339,16 @@
// For tests run exceed warnline but not timeout.
std::vector<std::tuple<std::string, int64_t, int>> timewarn_test_list;
- int num_testcases = testcase_list.size();
- int num_tests = 0;
- int num_success_tests = 0;
+ size_t testcase_count = testcase_list.size();
+ size_t test_count = 0;
+ size_t success_test_count = 0;
for (const auto& testcase : testcase_list) {
- num_tests += testcase.NumTests();
- for (int i = 0; i < testcase.NumTests(); ++i) {
+ test_count += testcase.TestCount();
+ for (size_t i = 0; i < testcase.TestCount(); ++i) {
TestResult result = testcase.GetTestResult(i);
if (result == TEST_SUCCESS) {
- ++num_success_tests;
+ ++success_test_count;
} else if (result == TEST_FAILED) {
fail_test_name_list.push_back(testcase.GetTestName(i));
} else if (result == TEST_TIMEOUT) {
@@ -361,20 +369,20 @@
}
ColoredPrintf(COLOR_GREEN, "[==========] ");
- printf("%d %s from %d %s ran.", num_tests, (num_tests == 1) ? "test" : "tests",
- num_testcases, (num_testcases == 1) ? "test case" : "test cases");
+ printf("%zu %s from %zu %s ran.", test_count, (test_count == 1) ? "test" : "tests",
+ testcase_count, (testcase_count == 1) ? "test case" : "test cases");
if (testing::GTEST_FLAG(print_time)) {
printf(" (%lld ms total)", elapsed_time / 1000000LL);
}
printf("\n");
ColoredPrintf(COLOR_GREEN, "[ PASSED ] ");
- printf("%d %s.\n", num_success_tests, (num_success_tests == 1) ? "test" : "tests");
+ printf("%zu %s.\n", success_test_count, (success_test_count == 1) ? "test" : "tests");
// Print tests failed.
- int num_fail_tests = fail_test_name_list.size();
- if (num_fail_tests > 0) {
+ size_t fail_test_count = fail_test_name_list.size();
+ if (fail_test_count > 0) {
ColoredPrintf(COLOR_RED, "[ FAILED ] ");
- printf("%d %s, listed below:\n", num_fail_tests, (num_fail_tests == 1) ? "test" : "tests");
+ printf("%zu %s, listed below:\n", fail_test_count, (fail_test_count == 1) ? "test" : "tests");
for (const auto& name : fail_test_name_list) {
ColoredPrintf(COLOR_RED, "[ FAILED ] ");
printf("%s\n", name.c_str());
@@ -382,10 +390,10 @@
}
// Print tests run timeout.
- int num_timeout_tests = timeout_test_list.size();
- if (num_timeout_tests > 0) {
+ size_t timeout_test_count = timeout_test_list.size();
+ if (timeout_test_count > 0) {
ColoredPrintf(COLOR_RED, "[ TIMEOUT ] ");
- printf("%d %s, listed below:\n", num_timeout_tests, (num_timeout_tests == 1) ? "test" : "tests");
+ printf("%zu %s, listed below:\n", timeout_test_count, (timeout_test_count == 1) ? "test" : "tests");
for (const auto& timeout_pair : timeout_test_list) {
ColoredPrintf(COLOR_RED, "[ TIMEOUT ] ");
printf("%s (stopped at %lld ms)\n", timeout_pair.first.c_str(),
@@ -394,10 +402,10 @@
}
// Print tests run exceed warnline.
- int num_timewarn_tests = timewarn_test_list.size();
- if (num_timewarn_tests > 0) {
+ size_t timewarn_test_count = timewarn_test_list.size();
+ if (timewarn_test_count > 0) {
ColoredPrintf(COLOR_YELLOW, "[ TIMEWARN ] ");
- printf("%d %s, listed below:\n", num_timewarn_tests, (num_timewarn_tests == 1) ? "test" : "tests");
+ printf("%zu %s, listed below:\n", timewarn_test_count, (timewarn_test_count == 1) ? "test" : "tests");
for (const auto& timewarn_tuple : timewarn_test_list) {
ColoredPrintf(COLOR_YELLOW, "[ TIMEWARN ] ");
printf("%s (%lld ms, exceed warnline %d ms)\n", std::get<0>(timewarn_tuple).c_str(),
@@ -406,14 +414,14 @@
}
}
- if (num_fail_tests > 0) {
- printf("\n%2d FAILED %s\n", num_fail_tests, (num_fail_tests == 1) ? "TEST" : "TESTS");
+ if (fail_test_count > 0) {
+ printf("\n%2zu FAILED %s\n", fail_test_count, (fail_test_count == 1) ? "TEST" : "TESTS");
}
- if (num_timeout_tests > 0) {
- printf("%2d TIMEOUT %s\n", num_timeout_tests, (num_timeout_tests == 1) ? "TEST" : "TESTS");
+ if (timeout_test_count > 0) {
+ printf("%2zu TIMEOUT %s\n", timeout_test_count, (timeout_test_count == 1) ? "TEST" : "TESTS");
}
- if (num_timewarn_tests > 0) {
- printf("%2d TIMEWARN %s\n", num_timewarn_tests, (num_timewarn_tests == 1) ? "TEST" : "TESTS");
+ if (timewarn_test_count > 0) {
+ printf("%2zu TIMEWARN %s\n", timewarn_test_count, (timewarn_test_count == 1) ? "TEST" : "TESTS");
}
fflush(stdout);
}
@@ -438,19 +446,20 @@
pid_t pid;
int64_t start_time;
int64_t deadline_time;
- int testcase_id, test_id;
+ size_t testcase_id, test_id;
bool done_flag;
- TestResult test_result;
+ bool timeout_flag;
+ int exit_status;
ChildProcInfo() : pid(0) {}
};
static void WaitChildProcs(std::vector<ChildProcInfo>& child_proc_list) {
pid_t result;
- int exit_status;
+ int status;
bool loop_flag = true;
while (true) {
- while ((result = waitpid(-1, &exit_status, WNOHANG)) == -1) {
+ while ((result = waitpid(-1, &status, WNOHANG)) == -1) {
if (errno != EINTR) {
break;
}
@@ -465,7 +474,7 @@
for (size_t i = 0; i < child_proc_list.size(); ++i) {
if (child_proc_list[i].deadline_time <= current_time) {
child_proc_list[i].done_flag = true;
- child_proc_list[i].test_result = TEST_TIMEOUT;
+ child_proc_list[i].timeout_flag = true;
loop_flag = false;
}
}
@@ -474,8 +483,8 @@
for (size_t i = 0; i < child_proc_list.size(); ++i) {
if (child_proc_list[i].pid == result) {
child_proc_list[i].done_flag = true;
- child_proc_list[i].test_result = (WEXITSTATUS(exit_status) == 0) ? TEST_SUCCESS :
- TEST_FAILED;
+ child_proc_list[i].timeout_flag = false;
+ child_proc_list[i].exit_status = status;
loop_flag = false;
break;
}
@@ -511,26 +520,32 @@
// We choose to use multi-fork and multi-wait here instead of multi-thread, because it always
// makes deadlock to use fork in multi-thread.
static void RunTestInSeparateProc(int argc, char** argv, std::vector<TestCase>& testcase_list,
- int num_iterations, int num_jobs) {
+ size_t iteration_count, size_t job_count) {
// Stop default result printer to avoid environment setup/teardown information for each test.
testing::UnitTest::GetInstance()->listeners().Release(
testing::UnitTest::GetInstance()->listeners().default_result_printer());
testing::UnitTest::GetInstance()->listeners().Append(new TestResultPrinter);
- for (int iteration = 1; iteration <= num_iterations; ++iteration) {
- OnTestIterationStartPrint(testcase_list, iteration, num_iterations);
+ for (size_t iteration = 1; iteration <= iteration_count; ++iteration) {
+ OnTestIterationStartPrint(testcase_list, iteration, iteration_count);
int64_t iteration_start_time = NanoTime();
- std::vector<ChildProcInfo> child_proc_list(num_jobs);
- int assign_testcase = 0, assign_test = 0;
- std::vector<int> finish_test_num_list(testcase_list.size(), 0);
- int num_finish_testcases = 0;
+ // Run up to job_count tests in parallel, each test in a child process.
+ std::vector<ChildProcInfo> child_proc_list(job_count);
- while (num_finish_testcases < static_cast<int>(testcase_list.size())) {
- // Fork child process up to num_jobs.
+ // Next test to run is [next_testcase_id:next_test_id].
+ size_t next_testcase_id = 0;
+ size_t next_test_id = 0;
+
+ // Record how many tests are finished.
+ std::vector<size_t> finished_test_count_list(testcase_list.size(), 0);
+ size_t finished_testcase_count = 0;
+
+ while (finished_testcase_count < testcase_list.size()) {
+ // Fork up to job_count child processes.
for (auto& child_proc : child_proc_list) {
- if (child_proc.pid == 0 && assign_testcase < static_cast<int>(testcase_list.size())) {
- std::string test_name = testcase_list[assign_testcase].GetTestName(assign_test);
+ if (child_proc.pid == 0 && next_testcase_id < testcase_list.size()) {
+ std::string test_name = testcase_list[next_testcase_id].GetTestName(next_test_id);
pid_t pid = fork();
if (pid == -1) {
perror("fork in RunTestInSeparateProc");
@@ -543,12 +558,12 @@
child_proc.pid = pid;
child_proc.start_time = NanoTime();
child_proc.deadline_time = child_proc.start_time + GetDeadlineInfo(test_name) * 1000000LL;
- child_proc.testcase_id = assign_testcase;
- child_proc.test_id = assign_test;
+ child_proc.testcase_id = next_testcase_id;
+ child_proc.test_id = next_test_id;
child_proc.done_flag = false;
- if (++assign_test == testcase_list[assign_testcase].NumTests()) {
- assign_test = 0;
- ++assign_testcase;
+ if (++next_test_id == testcase_list[next_testcase_id].TestCount()) {
+ next_test_id = 0;
+ ++next_testcase_id;
}
}
}
@@ -559,22 +574,31 @@
// Collect result.
for (auto& child_proc : child_proc_list) {
if (child_proc.pid != 0 && child_proc.done_flag == true) {
- int testcase_id = child_proc.testcase_id;
- int test_id = child_proc.test_id;
- TestResult test_result = child_proc.test_result;
- if (test_result == TEST_TIMEOUT) {
- // Kill and wait the child process.
+ size_t testcase_id = child_proc.testcase_id;
+ size_t test_id = child_proc.test_id;
+ TestCase& testcase = testcase_list[testcase_id];
+ testcase.SetTestTime(test_id, NanoTime() - child_proc.start_time);
+
+ if (child_proc.timeout_flag) {
+ // Kill and wait the timeout child process.
kill(child_proc.pid, SIGKILL);
WaitChildProc(child_proc.pid);
- }
- TestCase& testcase = testcase_list[testcase_id];
- testcase.SetTestResult(test_id, test_result);
- testcase.SetTestTime(test_id, NanoTime() - child_proc.start_time);
- if (test_result == TEST_TIMEOUT) {
+ testcase.SetTestResult(test_id, TEST_TIMEOUT);
OnTestTimeoutPrint(testcase, test_id);
+
+ } else if (WIFSIGNALED(child_proc.exit_status)) {
+ // Record signal terminated test as failed.
+ testcase.SetTestResult(test_id, TEST_FAILED);
+ OnTestTerminatedPrint(testcase, test_id, WTERMSIG(child_proc.exit_status));
+
+ } else {
+ testcase.SetTestResult(test_id, WEXITSTATUS(child_proc.exit_status) == 0 ?
+ TEST_SUCCESS : TEST_FAILED);
+ // TestResultPrinter::OnTestEnd has already printed result for normal exit.
}
- if (++finish_test_num_list[testcase_id] == testcase.NumTests()) {
- ++num_finish_testcases;
+
+ if (++finished_test_count_list[testcase_id] == testcase.TestCount()) {
+ ++finished_testcase_count;
}
child_proc.pid = 0;
child_proc.done_flag = false;
@@ -586,80 +610,64 @@
}
}
-static int GetCpuNumber() {
- FILE* fp = popen("cat /proc/cpuinfo | grep processor | wc -l", "r");
- int result = 1; // Return 1 if we can't get the exact cpu number.
- if (fp != NULL) {
- fscanf(fp, "%d", &result);
- if (result < 1) {
- result = 1;
- }
- pclose(fp);
- }
- return result;
+static size_t GetProcessorCount() {
+ return static_cast<size_t>(sysconf(_SC_NPROCESSORS_ONLN));
}
-// Pick options not for gtest; Return false if run error.
-// Use exit_flag to indicate whether we need to run gtest flow after PickOptions.
-static bool PickOptions(int* pargc, char*** pargv, bool* exit_flag) {
- int argc = *pargc;
- char** argv = *pargv;
+// Pick options not for gtest: There are two parts in argv, one part is handled by PickOptions()
+// as described in PrintHelpInfo(), the other part is handled by testing::InitGoogleTest() in
+// gtest. PickOptions() picks the first part of options and change them into flags and operations,
+// lefting the second part in argv.
+// Arguments:
+// argv is used to pass in all command arguments, and pass out only the part of options for gtest.
+// exit_flag is to indicate whether we need to run gtest workflow after PickOptions.
+// Return false if run error.
+static bool PickOptions(std::vector<char*>& argv, bool* exit_flag) {
*exit_flag = false;
- for (int i = 0; i < argc; ++i) {
+ for (size_t i = 1; i < argv.size() - 1; ++i) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
PrintHelpInfo();
return true;
}
}
- // Move --gtest_filter option to last, and add "-bionic_gtest*" to disable self test.
- int gtest_filter_option_pos = 0;
- for (int i = argc - 1; i >= 1; --i) {
+ // Move --gtest_filter option to last, and add "-bionic_selftest*" to disable self test.
+ std::string gtest_filter_str = "--gtest_filter=-bionic_selftest*";
+ for (size_t i = argv.size() - 2; i >= 1; --i) {
if (strncmp(argv[i], "--gtest_filter=", sizeof("--gtest_filter=") - 1) == 0) {
- gtest_filter_option_pos = i;
+ gtest_filter_str = std::string(argv[i]) + ":-bionic_selftest*";
+ argv.erase(argv.begin() + i);
break;
}
}
- if (gtest_filter_option_pos != 0) {
- char* gtest_filter_arg = argv[gtest_filter_option_pos];
- for (int i = gtest_filter_option_pos; i < argc - 1; ++i) {
- argv[i] = argv[i + 1];
- }
- char* new_arg = new char[strlen(gtest_filter_arg) + sizeof(":-bionic_gtest*")];
- strcpy(new_arg, gtest_filter_arg);
- strcat(new_arg, ":-bionic_gtest*");
- argv[argc - 1] = new_arg;
- } else {
- char** new_argv = new char* [argc + 1];
- for (int i = 0; i < argc; ++i) {
- new_argv[i] = argv[i];
- }
- new_argv[argc++] = const_cast<char*>("--gtest_filter=-bionic_gtest*");
- *pargv = argv = new_argv;
- }
+ argv.insert(argv.end() - 1, strdup(gtest_filter_str.c_str()));
- // Parse bionic_gtest specific options.
- bool isolate_option = false;
- int job_num_option = 1;
+ // Init default bionic_gtest option.
+ bool isolate_option = true;
+ size_t job_count_option = GetProcessorCount();
- int deadline_option_len = sizeof("--deadline=") - 1;
- int warnline_option_len = sizeof("--warnline=") - 1;
- int gtest_color_option_len = sizeof("--gtest_color=") - 1;
+ size_t deadline_option_len = strlen("--deadline=");
+ size_t warnline_option_len = strlen("--warnline=");
+ size_t gtest_color_option_len = strlen("--gtest_color=");
- for (int i = 1; i < argc; ++i) {
- int remove_arg_num = 0;
+ // Parse bionic_gtest specific options in arguments.
+ for (size_t i = 1; i < argv.size() - 1; ++i) {
+ if (strcmp(argv[i], "-j") == 0) {
+ isolate_option = true; // Enable isolation mode when -j is used.
+ int tmp;
+ if (argv[i + 1] != NULL && (tmp = atoi(argv[i + 1])) > 0) {
+ job_count_option = tmp;
+ argv.erase(argv.begin() + i);
+ } else {
+ job_count_option = GetProcessorCount();
+ }
+ argv.erase(argv.begin() + i);
+ --i;
- // If running in isolation mode, main process doesn't call testing::InitGoogleTest(&argc, argv).
- // So we should parse gtest options for printing here.
- if (strncmp(argv[i], "--gtest_color=", gtest_color_option_len) == 0) {
- testing::GTEST_FLAG(color) = argv[i] + gtest_color_option_len;
-
- } else if (strcmp(argv[i], "--gtest_print_time=0") == 0) {
- testing::GTEST_FLAG(print_time) = false;
-
- } else if (strcmp(argv[i], "--isolate") == 0) {
- isolate_option = true;
- remove_arg_num = 1;
+ } else if (strcmp(argv[i], "--no-isolate") == 0) {
+ isolate_option = false;
+ argv.erase(argv.begin() + i);
+ --i;
} else if (strncmp(argv[i], "--deadline=", deadline_option_len) == 0) {
global_test_run_deadline_in_ms = atoi(argv[i] + deadline_option_len);
@@ -668,7 +676,8 @@
argv[i] + deadline_option_len);
exit(1);
}
- remove_arg_num = 1;
+ argv.erase(argv.begin() + i);
+ --i;
} else if (strncmp(argv[i], "--warnline=", warnline_option_len) == 0) {
global_test_run_warnline_in_ms = atoi(argv[i] + warnline_option_len);
@@ -677,98 +686,107 @@
argv[i] + warnline_option_len);
exit(1);
}
- remove_arg_num = 1;
-
- } else if (strcmp(argv[i], "--bionic_gtest") == 0) {
- // Enable "bionic_gtest*" for self test.
- // Don't remove this option from argument list.
- argv[argc - 1] = const_cast<char*>("--gtest_filter=bionic_gtest*");
-
- } else if (strcmp(argv[i], "-j") == 0) {
- isolate_option = true; // Enable isolation mode when -j is used.
- if (i + 1 < argc && (job_num_option = atoi(argv[i + 1])) > 0) {
- remove_arg_num = 2;
- } else {
- job_num_option = GetCpuNumber();
- remove_arg_num = 1;
- }
- }
-
- if (remove_arg_num != 0) {
- for (int j = i; j < argc - remove_arg_num; ++j) {
- argv[j] = argv[j + remove_arg_num];
- }
- argc -= remove_arg_num;
+ argv.erase(argv.begin() + i);
--i;
+
+ } else if (strncmp(argv[i], "--gtest_color=", gtest_color_option_len) == 0) {
+ // If running in isolation mode, main process doesn't call testing::InitGoogleTest(&argc, argv).
+ // So we should parse gtest options for printing by ourselves.
+ testing::GTEST_FLAG(color) = argv[i] + gtest_color_option_len;
+
+ } else if (strcmp(argv[i], "--gtest_print_time=0") == 0) {
+ testing::GTEST_FLAG(print_time) = false;
+
+ } else if (strcmp(argv[i], "--gtest_list_tests") == 0) {
+ // Disable isolation mode in gtest_list_tests option.
+ isolate_option = false;
+
+ } else if (strcmp(argv[i], "--bionic-selftest") == 0) {
+ // This option is to enable "bionic_selftest*" for self test, and not shown in help informantion.
+ // Don't remove this option from argument list.
+ argv[argv.size() - 2] = strdup("--gtest_filter=bionic_selftest*");
}
}
// Handle --gtest_repeat=[COUNT] option if we are in isolation mode.
// We should check and remove this option to avoid child process running single test for several
// iterations.
- int gtest_repeat_num = 1;
+ size_t gtest_repeat_count = 1;
if (isolate_option == true) {
int len = sizeof("--gtest_repeat=") - 1;
- for (int i = 1; i < argc; ++i) {
+ for (size_t i = 1; i < argv.size() - 1; ++i) {
if (strncmp(argv[i], "--gtest_repeat=", len) == 0) {
- gtest_repeat_num = atoi(argv[i] + len);
- if (gtest_repeat_num < 0) {
+ int tmp = atoi(argv[i] + len);
+ if (tmp < 0) {
fprintf(stderr, "error count for option --gtest_repeat=[COUNT]\n");
return false;
}
- for (int j = i; j < argc - 1; ++j) {
- argv[j] = argv[j + 1];
- }
- --argc;
+ gtest_repeat_count = tmp;
+ argv.erase(argv.begin() + i);
break;
}
}
}
- *pargc = argc;
+ // Add --no-isolate option in argv to suppress subprocess running in isolation mode again.
+ // As DeathTest will try to execve again, this option should always be set.
+ argv.insert(argv.begin() + 1, strdup("--no-isolate"));
// Run tests in isolation mode.
if (isolate_option) {
*exit_flag = true;
std::vector<TestCase> testcase_list;
- if (EnumerateTests(argc, argv, testcase_list) == false) {
+ int argc = static_cast<int>(argv.size()) - 1;
+ if (EnumerateTests(argc, argv.data(), testcase_list) == false) {
return false;
}
- RunTestInSeparateProc(argc, argv, testcase_list, gtest_repeat_num, job_num_option);
+ RunTestInSeparateProc(argc, argv.data(), testcase_list, gtest_repeat_count, job_count_option);
return true;
}
return true;
}
int main(int argc, char** argv) {
+ std::vector<char*> arg_list;
+ for (int i = 0; i < argc; ++i) {
+ arg_list.push_back(argv[i]);
+ }
+ arg_list.push_back(NULL);
+
bool exit_flag;
int return_result = 0;
- if (PickOptions(&argc, &argv, &exit_flag) == false) {
+ if (PickOptions(arg_list, &exit_flag) == false) {
return_result = 1;
} else if (!exit_flag) {
- testing::InitGoogleTest(&argc, argv);
+ argc = static_cast<int>(arg_list.size()) - 1;
+ testing::InitGoogleTest(&argc, arg_list.data());
return_result = RUN_ALL_TESTS();
}
return return_result;
}
//################################################################################
-// Bionic Gtest self test, run this by --bionic_gtest --isolate option.
+// Bionic Gtest self test, run this by --bionic-selftest option.
-TEST(bionic_gtest, test_success) {
+TEST(bionic_selftest, test_success) {
ASSERT_EQ(1, 1);
}
-TEST(bionic_gtest, test_fail) {
+TEST(bionic_selftest, test_fail) {
ASSERT_EQ(0, 1);
}
-TEST(bionic_gtest, test_time_warn) {
+TEST(bionic_selftest, test_time_warn) {
sleep(4);
}
-TEST(bionic_gtest, test_timeout) {
+TEST(bionic_selftest, test_timeout) {
while (1) {}
}
+
+TEST(bionic_selftest, test_signal_SEGV_terminated) {
+ char* p = reinterpret_cast<char*>(static_cast<intptr_t>(atoi("0")));
+ *p = 3;
+}
diff --git a/tests/netdb_test.cpp b/tests/netdb_test.cpp
index ab5b487..a35f703 100644
--- a/tests/netdb_test.cpp
+++ b/tests/netdb_test.cpp
@@ -19,10 +19,16 @@
#include <gtest/gtest.h>
#include <arpa/inet.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+// https://code.google.com/p/android/issues/detail?id=13228
+TEST(netdb, freeaddrinfo_NULL) {
+ freeaddrinfo(NULL);
+}
+
TEST(netdb, getaddrinfo_NULL_host) {
// It's okay for the host argument to be NULL, as long as service isn't.
addrinfo* ai = NULL;
@@ -74,16 +80,34 @@
TEST(netdb, getaddrinfo_hints) {
addrinfo hints;
memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo* ai = NULL;
ASSERT_EQ(0, getaddrinfo( "localhost", "9999", &hints, &ai));
- ASSERT_EQ(AF_INET, ai->ai_family);
- ASSERT_EQ(SOCK_STREAM, ai->ai_socktype);
- ASSERT_EQ(IPPROTO_TCP, ai->ai_protocol);
- ASSERT_TRUE(ai->ai_next == NULL);
+ ASSERT_TRUE(ai != NULL);
+ // In glibc, getaddrinfo() converts ::1 to 127.0.0.1 for localhost,
+ // so one or two addrinfo may be returned.
+ addrinfo* tai = ai;
+ while (tai != NULL) {
+ ASSERT_EQ(AF_INET, tai->ai_family);
+ ASSERT_EQ(SOCK_STREAM, tai->ai_socktype);
+ ASSERT_EQ(IPPROTO_TCP, tai->ai_protocol);
+ tai = tai->ai_next;
+ }
+ freeaddrinfo(ai);
+}
+
+TEST(netdb, getaddrinfo_ip6_localhost) {
+ addrinfo* ai = NULL;
+ ASSERT_EQ(0, getaddrinfo("ip6-localhost", NULL, NULL, &ai));
+ ASSERT_TRUE(ai != NULL);
+ ASSERT_GE(ai->ai_addrlen, static_cast<socklen_t>(sizeof(sockaddr_in6)));
+ ASSERT_TRUE(ai->ai_addr != NULL);
+ sockaddr_in6 *addr = reinterpret_cast<sockaddr_in6*>(ai->ai_addr);
+ ASSERT_EQ(addr->sin6_family, AF_INET6);
+ ASSERT_EQ(0, memcmp(&addr->sin6_addr, &in6addr_loopback, sizeof(in6_addr)));
freeaddrinfo(ai);
}
@@ -116,8 +140,41 @@
ASSERT_EQ(EAI_FAMILY, getnameinfo(sa, too_little, tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST));
}
-void VerifyLocalhost(hostent *hent) {
+TEST(netdb, getnameinfo_localhost) {
+ sockaddr_in addr;
+ char host[NI_MAXHOST];
+ memset(&addr, 0, sizeof(sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(0x7f000001);
+ ASSERT_EQ(0, getnameinfo(reinterpret_cast<sockaddr*>(&addr), sizeof(addr),
+ host, sizeof(host), NULL, 0, 0));
+ ASSERT_STREQ(host, "localhost");
+}
+
+static void VerifyLocalhostName(const char* name) {
+ // Test possible localhost name and aliases, which depend on /etc/hosts or /system/etc/hosts.
+ ASSERT_TRUE(strcmp(name, "localhost") == 0 ||
+ strcmp(name, "ip6-localhost") == 0 ||
+ strcmp(name, "ip6-loopback") == 0) << name;
+}
+
+TEST(netdb, getnameinfo_ip6_localhost) {
+ sockaddr_in6 addr;
+ char host[NI_MAXHOST];
+ memset(&addr, 0, sizeof(sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_loopback;
+ ASSERT_EQ(0, getnameinfo(reinterpret_cast<sockaddr*>(&addr), sizeof(addr),
+ host, sizeof(host), NULL, 0, 0));
+ VerifyLocalhostName(host);
+}
+
+static void VerifyLocalhost(hostent *hent) {
ASSERT_TRUE(hent != NULL);
+ VerifyLocalhostName(hent->h_name);
+ for (size_t i = 0; hent->h_aliases[i] != NULL; ++i) {
+ VerifyLocalhostName(hent->h_aliases[i]);
+ }
ASSERT_EQ(hent->h_addrtype, AF_INET);
ASSERT_EQ(hent->h_addr[0], 127);
ASSERT_EQ(hent->h_addr[1], 0);
@@ -180,21 +237,18 @@
}
TEST(netdb, gethostbyaddr) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
- hostent *hp = gethostbyaddr(addr, sizeof(addr), AF_INET);
+ in_addr addr = { htonl(0x7f000001) };
+ hostent *hp = gethostbyaddr(&addr, sizeof(addr), AF_INET);
VerifyLocalhost(hp);
}
TEST(netdb, gethostbyaddr_r) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
-
+ in_addr addr = { htonl(0x7f000001) };
hostent hent;
hostent *hp;
char buf[512];
int err;
- int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+ int result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
ASSERT_EQ(0, result);
VerifyLocalhost(hp);
@@ -204,7 +258,7 @@
hostent hent2;
hostent *hp2;
char buf2[512];
- result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
+ result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
ASSERT_EQ(0, result);
VerifyLocalhost(hp2);
@@ -232,14 +286,12 @@
}
TEST(netdb, gethostbyaddr_r_ERANGE) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
-
+ in_addr addr = { htonl(0x7f000001) };
hostent hent;
hostent *hp;
char buf[4]; // Use too small buffer.
int err;
- int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+ int result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
ASSERT_EQ(ERANGE, result);
ASSERT_EQ(NULL, hp);
}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 1418c76..cb32079 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -32,7 +32,6 @@
#include <time.h>
#include <unistd.h>
-
TEST(pthread, pthread_key_create) {
pthread_key_t key;
ASSERT_EQ(0, pthread_key_create(&key, NULL));
@@ -432,7 +431,7 @@
ASSERT_EQ(ESRCH, pthread_detach(dead_thread));
}
-TEST(pthread, pthread_detach__leak) {
+TEST(pthread, pthread_detach_no_leak) {
size_t initial_bytes = 0;
// Run this loop more than once since the first loop causes some memory
// to be allocated permenantly. Run an extra loop to help catch any subtle
@@ -465,9 +464,7 @@
size_t final_bytes = mallinfo().uordblks;
int leaked_bytes = (final_bytes - initial_bytes);
- // User code (like this test) doesn't know how large pthread_internal_t is.
- // We can be pretty sure it's more than 128 bytes.
- ASSERT_LT(leaked_bytes, 32 /*threads*/ * 128 /*bytes*/);
+ ASSERT_EQ(0, leaked_bytes);
}
TEST(pthread, pthread_getcpuclockid__clock_gettime) {
@@ -633,18 +630,18 @@
ASSERT_EQ(default_stack_size, stack_size);
ASSERT_GE(GetActualStackSize(attributes), default_stack_size);
- // Large enough and a multiple of the page size.
+ // Large enough and a multiple of the page size; may be rounded up by pthread_create.
ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024));
ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
ASSERT_EQ(32*1024U, stack_size);
- ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
+ ASSERT_GE(GetActualStackSize(attributes), 32*1024U);
- // Large enough but not a multiple of the page size; will be rounded up by pthread_create.
+ // Large enough but not aligned; will be rounded up by pthread_create.
ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024 + 1));
ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
ASSERT_EQ(32*1024U + 1, stack_size);
#if defined(__BIONIC__)
- ASSERT_EQ(GetActualStackSize(attributes), 32*1024U + 1);
+ ASSERT_GT(GetActualStackSize(attributes), 32*1024U + 1);
#else // __BIONIC__
// glibc rounds down, in violation of POSIX. They document this in their BUGS section.
ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
@@ -939,6 +936,29 @@
ASSERT_EQ(6666U, stack_size);
}
+static void pthread_attr_getstack_18908062_helper(void*) {
+ char local_variable;
+ pthread_attr_t attributes;
+ pthread_getattr_np(pthread_self(), &attributes);
+ void* stack_base;
+ size_t stack_size;
+ pthread_attr_getstack(&attributes, &stack_base, &stack_size);
+
+ // Test whether &local_variable is in [stack_base, stack_base + stack_size).
+ ASSERT_LE(reinterpret_cast<char*>(stack_base), &local_variable);
+ ASSERT_LT(&local_variable, reinterpret_cast<char*>(stack_base) + stack_size);
+}
+
+// Check whether something on stack is in the range of
+// [stack_base, stack_base + stack_size). see b/18908062.
+TEST(pthread, pthread_attr_getstack_18908062) {
+ pthread_t t;
+ ASSERT_EQ(0, pthread_create(&t, NULL,
+ reinterpret_cast<void* (*)(void*)>(pthread_attr_getstack_18908062_helper),
+ NULL));
+ pthread_join(t, NULL);
+}
+
#if defined(__BIONIC__)
static void* pthread_gettid_np_helper(void* arg) {
*reinterpret_cast<pid_t*>(arg) = gettid();
diff --git a/tests/sys_personality_test.cpp b/tests/sys_personality_test.cpp
new file mode 100644
index 0000000..55a023d
--- /dev/null
+++ b/tests/sys_personality_test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <sys/personality.h>
+
+TEST(sys_personality, current_persona) {
+ int persona = personality(0xffffffff);
+#if defined(__BIONIC__)
+#if defined(__LP64__)
+ ASSERT_EQ(PER_LINUX, persona);
+#else
+ ASSERT_EQ(PER_LINUX32, persona);
+#endif
+#else
+ // GLIBC does not set persona prior process startup - it is always PER_LINUX;
+ ASSERT_EQ(PER_LINUX, persona);
+#endif
+}
diff --git a/tools/bionicbb/.gitignore b/tools/bionicbb/.gitignore
new file mode 100644
index 0000000..d0ff064
--- /dev/null
+++ b/tools/bionicbb/.gitignore
@@ -0,0 +1,58 @@
+config.py
+*.json
+oauth.storage
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
diff --git a/tools/bionicbb/README.md b/tools/bionicbb/README.md
new file mode 100644
index 0000000..91f64d8
--- /dev/null
+++ b/tools/bionicbb/README.md
@@ -0,0 +1,89 @@
+bionicbb
+========
+
+The bionic buildbot contains two services: a gmail polling service, and a web
+service that interacts with gerrit.
+
+Dependencies
+------------
+
+ * Python 2.7
+ * [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)
+ * [Requests](http://docs.python-requests.org/en/latest/)
+ * [termcolor](https://pypi.python.org/pypi/termcolor)
+
+Setup
+-----
+
+Create a `config.py` in the same directory as the sources. The structure of the
+configuration file is as follows:
+
+```python
+client_secret_file = 'CLIENT_SECRET_FILE.json'
+build_listener_url = 'BUILD_LISTENER_URL'
+jenkins_url = 'JENKINS_URL'
+jenkins_credentials = {
+ 'username': 'JENKINS_USERNAME',
+ 'password': 'JENKINS_PASSWORD',
+}
+```
+
+The client secret file comes from the Gmail API page of the [Google Developers
+Console](https://console.developers.google.com/). The Jenkins credentials are
+for a Jenkins account that has the appropriate permissions to launch the jobs
+the buildbot will use.
+
+You will also need to add the HTTP password for the buildbot's Gerrit account to
+`~/.netrc`. The HTTP password can be obtained from the [Gerrit HTTP password
+settings](https://android-review.googlesource.com/#/settings/http-password).
+
+To launch the services:
+
+```bash
+$ python build_listener.py >build.log 2>&1 &
+$ python gmail_listener.py >mail.log 2>&1 &
+```
+
+The mail listener will direct your browser to an authentication page for the
+Gmail API.
+
+gmail\_listener.py
+------------------
+
+Bionicbb polls a gmail account to find changes that need to be built. The gmail
+account needs to have a gerrit account set up with project watches on anything
+it finds interesting. This is a rather ugly hack, but it seems to be the
+simplest option available.
+
+Gerrit does offer a streaming notification service that would be _far_ better,
+but it is only available over an SSH conection to gerrit, and the AOSP gerrit
+does not support this connection.
+
+Another option would be polling gerrit itself, but we'd have to process each
+change every time to see if it should be built, whereas project watches allow us
+to treat these as semi-push notifications (we still have to poll gmail).
+
+One drawback to this approach is that it's a hassle to set up the project
+watches for a large number of projects. Since bionicbb is only interested in a
+small subset of projects, this is a non-issue.
+
+If the buildbot has applied Verified-1 to a patchset, the user may add their own
+Verified+1 to the change and the buildbot will remove its rejection the next
+time the services polls (by default, every five minutes).
+
+The service will also listen for the following commands:
+
+ * `bionicbb:clean`: Something is very broken and the buildbot's output
+ directory needs to be nuked.
+ * `bionicbb:retry`: Something went wrong and the buildbot should retry the
+ build.
+
+build\_listener.py
+------------------
+
+The build listener service responds to HTTP POST events sent from Jenkins and
+updates CLs accordingly. The only other API endpoint is `/drop-rejection`, which
+will remove a Verified-1 from a previously rejected patchset. The actually
+invocation of this is handled by the gmail listener.
diff --git a/tools/bionicbb/build_listener.py b/tools/bionicbb/build_listener.py
new file mode 100644
index 0000000..f7f52ed
--- /dev/null
+++ b/tools/bionicbb/build_listener.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python2
+#
+# 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 json
+import requests
+import termcolor
+
+import gerrit
+
+from flask import Flask, request
+app = Flask(__name__)
+
+
+def gerrit_url(endpoint):
+ gerrit_base_url = 'https://android-review.googlesource.com'
+ return gerrit_base_url + endpoint
+
+
+@app.route('/', methods=['POST'])
+def handle_build_message():
+ result = json.loads(request.data)
+
+ name = result['name']
+ number = result['build']['number']
+ status = result['build']['status']
+ go_url = 'http://go/bionicbb/' + result['build']['url']
+ full_url = result['build']['full_url']
+ params = result['build']['parameters']
+ change_id = params['CHANGE_ID']
+ ref = params['REF']
+ patch_set = ref.split('/')[-1]
+
+ print '{} #{} {}: {}'.format(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.
+ if name == 'bionic-presubmit':
+ message_lines = ['{} #{} checkbuild {}: {}'.format(
+ name, number, status, go_url)]
+ if status == 'FAILURE':
+ message_lines += ['If you believe this Verified-1 was in error, '
+ '+1 the change and bionicbb will remove the -1 '
+ 'shortly.']
+
+ request_data = {
+ 'message': '\n'.join(message_lines)
+ }
+
+ label = 'Verified'
+ if status == 'FAILURE':
+ request_data['labels'] = {label: -1}
+ elif status == 'SUCCESS':
+ request_data['labels'] = {label: +1}
+
+ 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)
+ 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)
+ elif name == 'bionic-lint':
+ print 'IGNORED'
+ else:
+ print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name)
+ return ''
+
+
+@app.route('/drop-rejection', methods=['POST'])
+def drop_rejection():
+ revision_info = json.loads(request.data)
+
+ change_id = revision_info['changeid']
+ patch_set = revision_info['patchset']
+
+ bb_email = 'bionicbb@android.com'
+ labels = gerrit.get_labels(change_id, patch_set)
+ if bb_email in labels['Verified']:
+ bb_review = labels['Verified'][bb_email]
+ else:
+ bb_review = 0
+
+ if bb_review >= 0:
+ print 'No rejection to drop: {} {}'.format(change_id, patch_set)
+ return ''
+
+ print 'Dropping rejection: {} {}'.format(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)
+ return ''
+
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', debug=True)
diff --git a/tools/bionicbb/gerrit.py b/tools/bionicbb/gerrit.py
new file mode 100644
index 0000000..76e42b4
--- /dev/null
+++ b/tools/bionicbb/gerrit.py
@@ -0,0 +1,74 @@
+#
+# 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 json
+import requests
+
+
+class GerritError(RuntimeError):
+ def __init__(self, code, url):
+ self.code = code
+ self.url = url
+ super(GerritError, self).__init__('Error {}: {}'.format(code, url))
+
+
+def get_commit(change_id, revision):
+ return json.loads(
+ call('/changes/{}/revisions/{}/commit'.format(change_id, revision)))
+
+
+def call(endpoint, method='GET'):
+ if method != 'GET':
+ raise NotImplementedError('Currently only HTTP GET is supported.')
+ gerrit_url = 'https://android-review.googlesource.com'
+ url = gerrit_url + endpoint
+ response = requests.get(url)
+ if response.status_code != 200:
+ raise GerritError(response.status_code, url)
+ return response.text[5:]
+
+
+def ref_for_change(change_id):
+ endpoint = '/changes/{}/detail?o=CURRENT_REVISION'.format(change_id)
+ change = json.loads(call(endpoint))
+ commit = change['current_revision']
+ return change['revisions'][commit]['fetch']['http']['ref']
+
+
+def get_labels(change_id, patch_set):
+ """Returns labels attached to a revision.
+
+ Returned data is in the following format:
+ {
+ 'Code-Review': {
+ <email>: <value>,
+ ...
+ },
+ 'Verified': {
+ <email>: <value>,
+ ...
+ }
+ }
+ """
+ details = 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:
+ labels['Code-Review'][review['email']] = int(review['value'])
+ for review in details['labels']['Verified']['all']:
+ if 'value' in review and 'email' in review:
+ labels['Verified'][review['email']] = int(review['value'])
+ return labels
diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py
new file mode 100644
index 0000000..6a8b9e6
--- /dev/null
+++ b/tools/bionicbb/gmail_listener.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python2
+#
+# 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 httplib
+import httplib2
+import jenkinsapi
+import json
+import re
+import requests
+import termcolor
+import socket
+import sys
+import time
+
+import apiclient.errors
+
+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
+
+
+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 '
+ 'changes, patch sets, and commits.')
+
+
+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(gerrit_info, dry_run):
+ username = config.jenkins_credentials['username']
+ password = config.jenkins_credentials['password']
+ jenkins_url = config.jenkins_url
+ jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
+
+ build = 'clean-bionic-presubmit'
+ if build in jenkins:
+ if not dry_run:
+ job = jenkins[build].invoke()
+ url = job.get_build().baseurl
+ else:
+ url = 'DRY_RUN_URL'
+ print '{}({}): {} {}'.format(
+ termcolor.colored('CLEAN', 'green'),
+ gerrit_info['MessageType'],
+ build,
+ url)
+ else:
+ print '{}({}): {}'.format(
+ termcolor.colored('CLEAN', 'red'),
+ gerrit_info['MessageType'],
+ termcolor.colored(build, 'red'))
+ return True
+
+
+def build_project(gerrit_info, dry_run):
+ project_to_jenkins_map = {
+ 'platform/bionic': 'bionic-presubmit',
+ 'platform/build': 'bionic-presubmit',
+ 'platform/external/jemalloc': 'bionic-presubmit',
+ 'platform/external/libcxx': 'bionic-presubmit',
+ 'platform/external/libcxxabi': 'bionic-presubmit',
+ 'platform/external/compiler-rt': 'bionic-presubmit',
+ }
+
+ username = config.jenkins_credentials['username']
+ password = config.jenkins_credentials['password']
+ jenkins_url = config.jenkins_url
+ jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
+
+ project = gerrit_info['Project']
+ change_id = gerrit_info['Change-Id']
+ if project in project_to_jenkins_map:
+ build = project_to_jenkins_map[project]
+ else:
+ build = 'bionic-presubmit'
+
+ if build in jenkins:
+ project_path = '/'.join(project.split('/')[1:])
+ 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
+ params = {
+ 'REF': ref,
+ 'CHANGE_ID': change_id,
+ 'PROJECT': project_path
+ }
+ if not dry_run:
+ job = jenkins[build].invoke(build_params=params)
+ url = job.get_build().baseurl
+ else:
+ url = 'DRY_RUN_URL'
+ print '{}({}): {} => {} {} {}'.format(
+ termcolor.colored('BUILD', 'green'),
+ gerrit_info['MessageType'],
+ project,
+ build,
+ url,
+ change_id)
+ else:
+ print '{}({}): {} => {} {}'.format(
+ termcolor.colored('BUILD', 'red'),
+ gerrit_info['MessageType'],
+ project,
+ termcolor.colored(build, 'red'),
+ change_id)
+ return True
+
+
+def handle_change(gerrit_info, _, dry_run):
+ if should_skip_message(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):
+ request_data = {
+ 'changeid': gerrit_info['Change-Id'],
+ 'patchset': gerrit_info['PatchSet']
+ }
+ url = '{}/{}'.format(config.build_listener_url, 'drop-rejection')
+ headers = {'Content-Type': 'application/json;charset=UTF-8'}
+ if not 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)
+ return False
+ print '{}({}): {}'.format(
+ termcolor.colored('CHECK', 'green'),
+ gerrit_info['MessageType'],
+ gerrit_info['Change-Id'])
+ return True
+
+
+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):
+ return True
+
+ command_map = {
+ 'clean': lambda: clean_project(gerrit_info, dry_run),
+ 'retry': lambda: build_project(gerrit_info, dry_run),
+ }
+
+ def handle_unknown_command():
+ pass # TODO(danalbert): should complain to the commenter.
+
+ commands = [match.group(1).strip() for match in
+ re.finditer(r'^bionicbb:\s*(.+)$', body, flags=re.MULTILINE)]
+
+ for command in commands:
+ if command in command_map:
+ command_map[command]()
+ else:
+ handle_unknown_command()
+
+ return True
+
+
+def skip_handler(gerrit_info, _, __):
+ print '{}({}): {}'.format(
+ termcolor.colored('SKIP', 'yellow'),
+ gerrit_info['MessageType'],
+ gerrit_info['Change-Id'])
+ return True
+handle_abandon = 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:
+ print termcolor.colored('No info found: {}'.format(msg['id']),
+ 'red')
+ msg_type = gerrit_info['MessageType']
+ handler = 'handle_{}'.format(gerrit_info['MessageType'])
+ if handler in globals():
+ return globals()[handler](gerrit_info, body, dry_run)
+ else:
+ print termcolor.colored(
+ 'MessageType {} unhandled.'.format(msg_type), 'red')
+ print
+ return False
+ except NotImplementedError as ex:
+ print ex
+ return False
+
+
+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:
+ print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex)
+ time.sleep(60 * 5)
+ except apiclient.errors.HttpError as ex:
+ print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), 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/test_gmail_listener.py b/tools/bionicbb/test_gmail_listener.py
new file mode 100644
index 0000000..6545cdc
--- /dev/null
+++ b/tools/bionicbb/test_gmail_listener.py
@@ -0,0 +1,64 @@
+import gmail_listener
+import mock
+import unittest
+
+
+class TestShouldSkipMessage(unittest.TestCase):
+ def test_accepts_googlers(self):
+ 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_message({
+ 'MessageType': message_type,
+ 'Change-Id': '',
+ 'PatchSet': '',
+ }))
+
+ 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_message({
+ 'MessageType': message_type,
+ 'Change-Id': '',
+ 'PatchSet': '',
+ }))
+
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ mock_commit.return_value = {
+ 'committer': {'email': 'johndoe@example.com'}
+ }
+
+ 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')
+
+ 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')
+
+
+if __name__ == '__main__':
+ unittest.main()