Compare commits
140 Commits
android-we
...
master-soo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
635ff5fdac | ||
|
|
a56904273c | ||
|
|
f93bddbbd8 | ||
|
|
2803a15d4c | ||
|
|
70f11bd69b | ||
|
|
8f7a4a3cb5 | ||
|
|
649a2ea0e2 | ||
|
|
c371306714 | ||
|
|
87e33892fc | ||
|
|
7454daa97f | ||
|
|
dab6ead2aa | ||
|
|
7d0b7b4ba2 | ||
|
|
98f4e07237 | ||
|
|
b624072cbd | ||
|
|
0ecd342743 | ||
|
|
faa14d4de8 | ||
|
|
abc97e2e71 | ||
|
|
6549fe249c | ||
|
|
c15e8fdb8d | ||
|
|
fa6f649607 | ||
|
|
e1a61f5e93 | ||
|
|
0bf650373e | ||
|
|
11331f60dd | ||
|
|
f8093a9485 | ||
|
|
b904afa16e | ||
|
|
9f0d99d298 | ||
|
|
62d6533c1a | ||
|
|
bed7a7e5eb | ||
|
|
a85bcc2e99 | ||
|
|
6fa65e7cd5 | ||
|
|
e0905c94d3 | ||
|
|
520cbf51b1 | ||
|
|
42d7468f99 | ||
|
|
b293969c6d | ||
|
|
032907d8c7 | ||
|
|
2ea504fed1 | ||
|
|
59bce688c7 | ||
|
|
bfd65279a5 | ||
|
|
682a240c5d | ||
|
|
1d76f1cc8b | ||
|
|
42502d702e | ||
|
|
2eaff07839 | ||
|
|
f327fae69c | ||
|
|
9a2744df30 | ||
|
|
d1aea30b2a | ||
|
|
2c256a0f09 | ||
|
|
440242f038 | ||
|
|
e91e66f223 | ||
|
|
ea295f68f1 | ||
|
|
ebfb55e4cd | ||
|
|
6612d7a347 | ||
|
|
2bb93482a7 | ||
|
|
b0b9338ff8 | ||
|
|
d2177404e2 | ||
|
|
6c63ee41ac | ||
|
|
094f58fb2a | ||
|
|
41ebceaf3a | ||
|
|
adfcb97317 | ||
|
|
e3ecedd306 | ||
|
|
0a93df369c | ||
|
|
3da9373fe0 | ||
|
|
7280e507b6 | ||
|
|
3391a9ff13 | ||
|
|
dc405b5230 | ||
|
|
6df122f852 | ||
|
|
ff18108981 | ||
|
|
18870d350c | ||
|
|
9ceec1a75d | ||
|
|
f15ceeb784 | ||
|
|
457c34ad84 | ||
|
|
913fe559f6 | ||
|
|
7592008030 | ||
|
|
2a6342187a | ||
|
|
a00a9f0b7e | ||
|
|
6fb8e96e5f | ||
|
|
da3c4f2f0d | ||
|
|
bb25bbe19e | ||
|
|
0776f0f6e2 | ||
|
|
3d7bea1fa0 | ||
|
|
3244d9f07f | ||
|
|
c5bd96efd2 | ||
|
|
72a6fdcdc7 | ||
|
|
af7538b496 | ||
|
|
4ae773633a | ||
|
|
7a8c7c48db | ||
|
|
21988a3b16 | ||
|
|
cb6ae56b3e | ||
|
|
d3fe4f1229 | ||
|
|
42281880a8 | ||
|
|
3875744f89 | ||
|
|
fc4850e37b | ||
|
|
2a81536144 | ||
|
|
a9325133aa | ||
|
|
31128da28f | ||
|
|
f84a5c6c5c | ||
|
|
a4061cddbe | ||
|
|
14af27a147 | ||
|
|
e7e1c875b0 | ||
|
|
a40cb0ca7f | ||
|
|
12d8902745 | ||
|
|
2587c6a2f0 | ||
|
|
95f1ee235a | ||
|
|
c0f89283cc | ||
|
|
43e020ce93 | ||
|
|
c6ccdfaf1f | ||
|
|
163ab8ba86 | ||
|
|
66aa0b61f7 | ||
|
|
cafc948069 | ||
|
|
940d3122c9 | ||
|
|
a529efac4e | ||
|
|
605ee81b06 | ||
|
|
8f3f04184a | ||
|
|
5ddbb3f936 | ||
|
|
447cd19681 | ||
|
|
a0ecd5b2b4 | ||
|
|
de88974120 | ||
|
|
c921eb6770 | ||
|
|
d032378790 | ||
|
|
9ce9bf5aec | ||
|
|
ef25592f14 | ||
|
|
c0e7dbb1db | ||
|
|
0a92ac8848 | ||
|
|
9f2c2f53d3 | ||
|
|
f9ff2eeaee | ||
|
|
aba687a09c | ||
|
|
694282b172 | ||
|
|
4d3abcb033 | ||
|
|
41efc92e35 | ||
|
|
5cf46f81ea | ||
|
|
76615dae93 | ||
|
|
dadac10fcc | ||
|
|
90d6279802 | ||
|
|
22d8776587 | ||
|
|
51b8912253 | ||
|
|
270f2ea800 | ||
|
|
7357ad0875 | ||
|
|
959bc099a3 | ||
|
|
68a3b658b1 | ||
|
|
062d498e28 | ||
|
|
d2b8741e1b |
1
Android.bp
Normal file
1
Android.bp
Normal file
@@ -0,0 +1 @@
|
||||
subdirs = ["*"]
|
||||
123
benchmarks/Android.bp
Normal file
123
benchmarks/Android.bp
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright (C) 2013 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Benchmarks library, usable by projects outside this directory.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
benchmark_cflags = [
|
||||
"-O2",
|
||||
"-fno-builtin",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wunused",
|
||||
]
|
||||
|
||||
benchmark_cppflags = ["-std=gnu++11"]
|
||||
|
||||
benchmarklib_src_files = [
|
||||
"Benchmark.cpp",
|
||||
"utils.cpp",
|
||||
"main.cpp",
|
||||
]
|
||||
|
||||
cc_library_static {
|
||||
name: "libbenchmark",
|
||||
host_supported: true,
|
||||
cflags: benchmark_cflags,
|
||||
cppflags: benchmark_cppflags,
|
||||
srcs: benchmarklib_src_files,
|
||||
static_libs: ["libbase"],
|
||||
export_include_dirs: ["."],
|
||||
target: {
|
||||
darwin: {
|
||||
// Only supported on linux systems.
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Benchmarks.
|
||||
// -----------------------------------------------------------------------------
|
||||
benchmark_src_files = [
|
||||
"math_benchmark.cpp",
|
||||
"property_benchmark.cpp",
|
||||
"pthread_benchmark.cpp",
|
||||
"semaphore_benchmark.cpp",
|
||||
"stdio_benchmark.cpp",
|
||||
"string_benchmark.cpp",
|
||||
"time_benchmark.cpp",
|
||||
"unistd_benchmark.cpp",
|
||||
]
|
||||
|
||||
// Build benchmarks for the device (with bionic's .so). Run with:
|
||||
// adb shell bionic-benchmarks32
|
||||
// adb shell bionic-benchmarks64
|
||||
cc_binary {
|
||||
name: "bionic-benchmarks",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
compile_multilib: "both",
|
||||
cflags: benchmark_cflags,
|
||||
cppflags: benchmark_cppflags,
|
||||
srcs: benchmark_src_files,
|
||||
static_libs: [
|
||||
"libbenchmark",
|
||||
"libbase",
|
||||
],
|
||||
}
|
||||
|
||||
// We don't build a static benchmark executable because it's not usually
|
||||
// useful. If you're trying to run the current benchmarks on an older
|
||||
// release, it's (so far at least) always because you want to measure the
|
||||
// performance of the old release's libc, and a static benchmark isn't
|
||||
// going to let you do that.
|
||||
|
||||
// Build benchmarks for the host (against glibc!). Run with:
|
||||
cc_binary_host {
|
||||
name: "bionic-benchmarks-glibc",
|
||||
multilib: {
|
||||
lib32: {
|
||||
stem: "bionic-benchmarks-glibc32",
|
||||
},
|
||||
lib64: {
|
||||
stem: "bionic-benchmarks-glibc64",
|
||||
},
|
||||
},
|
||||
compile_multilib: "both",
|
||||
cflags: benchmark_cflags,
|
||||
cppflags: benchmark_cppflags,
|
||||
ldflags: ["-lrt"],
|
||||
srcs: benchmark_src_files,
|
||||
static_libs: [
|
||||
"libbenchmark",
|
||||
"libbase",
|
||||
],
|
||||
target: {
|
||||
darwin: {
|
||||
// Only supported on linux systems.
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
2261
libc/Android.bp
Normal file
2261
libc/Android.bp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -63,14 +63,22 @@ libc_common_src_files := \
|
||||
stdio/sprintf.c \
|
||||
stdio/stdio.c \
|
||||
stdio/stdio_ext.cpp \
|
||||
stdlib/atexit.c \
|
||||
stdlib/exit.c \
|
||||
|
||||
# Fortify implementations of libc functions.
|
||||
libc_common_src_files += \
|
||||
bionic/__FD_chk.cpp \
|
||||
bionic/__fgets_chk.cpp \
|
||||
bionic/__memchr_chk.cpp \
|
||||
bionic/__memmove_chk.cpp \
|
||||
bionic/__memrchr_chk.cpp \
|
||||
bionic/__poll_chk.cpp \
|
||||
bionic/__pread64_chk.cpp \
|
||||
bionic/__pread_chk.cpp \
|
||||
bionic/__read_chk.cpp \
|
||||
bionic/__readlink_chk.cpp \
|
||||
bionic/__readlinkat_chk.cpp \
|
||||
bionic/__recvfrom_chk.cpp \
|
||||
bionic/__stpcpy_chk.cpp \
|
||||
bionic/__stpncpy_chk.cpp \
|
||||
@@ -104,6 +112,7 @@ libc_bionic_ndk_src_files := \
|
||||
bionic/clock_getcpuclockid.cpp \
|
||||
bionic/clock_nanosleep.cpp \
|
||||
bionic/clone.cpp \
|
||||
bionic/close.cpp \
|
||||
bionic/__cmsg_nxthdr.cpp \
|
||||
bionic/connect.cpp \
|
||||
bionic/ctype.cpp \
|
||||
@@ -476,11 +485,9 @@ libc_upstream_openbsd_ndk_src_files := \
|
||||
upstream-openbsd/lib/libc/stdio/wprintf.c \
|
||||
upstream-openbsd/lib/libc/stdio/wscanf.c \
|
||||
upstream-openbsd/lib/libc/stdio/wsetup.c \
|
||||
upstream-openbsd/lib/libc/stdlib/atexit.c \
|
||||
upstream-openbsd/lib/libc/stdlib/atoi.c \
|
||||
upstream-openbsd/lib/libc/stdlib/atol.c \
|
||||
upstream-openbsd/lib/libc/stdlib/atoll.c \
|
||||
upstream-openbsd/lib/libc/stdlib/exit.c \
|
||||
upstream-openbsd/lib/libc/stdlib/getenv.c \
|
||||
upstream-openbsd/lib/libc/stdlib/insque.c \
|
||||
upstream-openbsd/lib/libc/stdlib/lsearch.c \
|
||||
@@ -820,12 +827,7 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(libc_upstream_openbsd_ndk_src_files)
|
||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
||||
LOCAL_CLANG := false
|
||||
else
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
endif
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
$(libc_common_cflags) \
|
||||
@@ -863,12 +865,7 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(libc_upstream_openbsd_src_files)
|
||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
||||
LOCAL_CLANG := false
|
||||
else
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
endif
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
$(libc_common_cflags) \
|
||||
@@ -908,12 +905,7 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES_32 := $(libc_upstream_openbsd_gdtoa_src_files_32)
|
||||
LOCAL_SRC_FILES_64 := $(libc_upstream_openbsd_gdtoa_src_files_64)
|
||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
||||
LOCAL_CLANG := false
|
||||
else
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
endif
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
$(libc_common_cflags) \
|
||||
@@ -949,9 +941,6 @@ LOCAL_SRC_FILES := $(libc_bionic_src_files)
|
||||
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||
-Wframe-larger-than=2048 \
|
||||
|
||||
# ssse3-strcmp-slm.S does not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as
|
||||
|
||||
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||
@@ -983,9 +972,6 @@ LOCAL_SRC_FILES := $(libc_bionic_ndk_src_files)
|
||||
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||
-Wframe-larger-than=2048 \
|
||||
|
||||
# ssse3-strcmp-slm.S does not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as
|
||||
|
||||
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||
@@ -1007,7 +993,7 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files)
|
||||
LOCAL_CFLAGS := $(libc_common_cflags) -fno-data-sections -Wframe-larger-than=2048
|
||||
LOCAL_CFLAGS := $(libc_common_cflags) -Wframe-larger-than=2048
|
||||
|
||||
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
|
||||
LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
|
||||
@@ -1038,9 +1024,6 @@ LOCAL_SRC_FILES := $(libc_pthread_src_files)
|
||||
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||
-Wframe-larger-than=2048 \
|
||||
|
||||
# ssse3-strcmp-slm.S does not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_x86_64 += -no-integrated-as
|
||||
|
||||
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||
@@ -1232,10 +1215,6 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi
|
||||
|
||||
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||
endif
|
||||
|
||||
LOCAL_CXX_STL := none
|
||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||
|
||||
@@ -1328,6 +1307,11 @@ LOCAL_MODULE := libc
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
||||
|
||||
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||
endif
|
||||
|
||||
LOCAL_CXX_STL := none
|
||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||
LOCAL_ADDRESS_SANITIZER := false
|
||||
@@ -1349,10 +1333,13 @@ LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
||||
|
||||
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||
LOCAL_SRC_FILES := \
|
||||
arch-common/bionic/crtbegin_so.c \
|
||||
arch-common/bionic/crtbrand.S \
|
||||
$(libc_arch_dynamic_src_files) \
|
||||
bionic/malloc_debug_common.cpp \
|
||||
bionic/libc_init_dynamic.cpp \
|
||||
bionic/NetdClient.cpp \
|
||||
arch-common/bionic/crtend_so.S \
|
||||
|
||||
LOCAL_MODULE := libc
|
||||
LOCAL_CLANG := $(use_clang)
|
||||
@@ -1365,6 +1352,9 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||
# meaningful name resolution.
|
||||
LOCAL_STRIP_MODULE := keep_symbols
|
||||
|
||||
# Do not pack libc.so relocations; see http://b/20645321 for details.
|
||||
LOCAL_PACK_MODULE_RELOCATIONS := false
|
||||
|
||||
# WARNING: The only library libc.so should depend on is libdl.so! If you add other libraries,
|
||||
# make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries. This
|
||||
# ensures that symbols that are pulled into those new libraries from libgcc.a are not declared
|
||||
@@ -1375,6 +1365,11 @@ LOCAL_STRIP_MODULE := keep_symbols
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libdl
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
||||
|
||||
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||
endif
|
||||
|
||||
LOCAL_CXX_STL := none
|
||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||
|
||||
@@ -1392,15 +1387,15 @@ LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv
|
||||
|
||||
$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
|
||||
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files))
|
||||
|
||||
LOCAL_NO_CRT := true
|
||||
LOCAL_ASFLAGS += $(libc_crt_target_cflags)
|
||||
|
||||
# special for arm
|
||||
LOCAL_NO_CRT_arm := true
|
||||
LOCAL_CFLAGS_arm += -DCRT_LEGACY_WORKAROUND
|
||||
LOCAL_ASFLAGS_arm += $(libc_crt_target_cflags)
|
||||
LOCAL_SRC_FILES_arm += \
|
||||
arch-common/bionic/crtbegin_so.c \
|
||||
arch-common/bionic/crtbrand.S \
|
||||
arch-arm/bionic/atexit_legacy.c \
|
||||
arch-common/bionic/crtend_so.S
|
||||
arch-arm/bionic/atexit_legacy.c
|
||||
|
||||
LOCAL_ADDRESS_SANITIZER := false
|
||||
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ ssize_t pread64(int, void*, size_t, off64_t) arm,mips,x86
|
||||
ssize_t pread64|pread(int, void*, size_t, off_t) arm64,mips64,x86_64
|
||||
ssize_t pwrite64(int, void*, size_t, off64_t) arm,mips,x86
|
||||
ssize_t pwrite64|pwrite(int, void*, size_t, off_t) arm64,mips64,x86_64
|
||||
int close(int) all
|
||||
int ___close:close(int) all
|
||||
pid_t __getpid:getpid() all
|
||||
int munmap(void*, size_t) all
|
||||
void* mremap(void*, size_t, size_t, unsigned long) all
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
arch-arm/cortex-a15/bionic/memcpy.S \
|
||||
arch-arm/cortex-a15/bionic/memset.S \
|
||||
arch-arm/cortex-a15/bionic/stpcpy.S \
|
||||
@@ -10,5 +9,8 @@ libc_bionic_src_files_arm += \
|
||||
arch-arm/cortex-a15/bionic/__strcpy_chk.S \
|
||||
arch-arm/cortex-a15/bionic/strlen.S \
|
||||
|
||||
libc_openbsd_src_files_arm += \
|
||||
upstream-openbsd/lib/libc/string/memmove.c \
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/denver/bionic/memmove.S \
|
||||
|
||||
@@ -44,7 +44,7 @@ ENTRY_PRIVATE(MEMCPY_BASE)
|
||||
/* check if buffers are aligned. If so, run arm-only version */
|
||||
eor r3, r0, r1
|
||||
ands r3, r3, #0x3
|
||||
beq __memcpy_base_aligned
|
||||
beq MEMCPY_BASE_ALIGNED
|
||||
|
||||
/* Check the upper size limit for Neon unaligned memory access in memcpy */
|
||||
cmp r2, #224
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
arch-arm/cortex-a9/bionic/memcpy.S \
|
||||
arch-arm/cortex-a9/bionic/memset.S \
|
||||
arch-arm/cortex-a9/bionic/stpcpy.S \
|
||||
@@ -10,5 +9,8 @@ libc_bionic_src_files_arm += \
|
||||
arch-arm/cortex-a9/bionic/__strcpy_chk.S \
|
||||
arch-arm/cortex-a9/bionic/strlen.S \
|
||||
|
||||
libc_openbsd_src_files_arm += \
|
||||
upstream-openbsd/lib/libc/string/memmove.c \
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/denver/bionic/memmove.S \
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
arch-arm/krait/bionic/memcpy.S \
|
||||
arch-arm/krait/bionic/memset.S \
|
||||
arch-arm/krait/bionic/strcmp.S \
|
||||
@@ -13,5 +12,8 @@ libc_bionic_src_files_arm += \
|
||||
arch-arm/cortex-a15/bionic/strcpy.S \
|
||||
arch-arm/cortex-a15/bionic/strlen.S \
|
||||
|
||||
libc_openbsd_src_files_arm += \
|
||||
upstream-openbsd/lib/libc/string/memmove.c \
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/generic/bionic/memcmp.S \
|
||||
|
||||
libc_bionic_src_files_arm += \
|
||||
arch-arm/denver/bionic/memmove.S \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
mov ip, r7
|
||||
ldr r7, =__NR_close
|
||||
swi #0
|
||||
@@ -11,4 +11,5 @@ ENTRY(close)
|
||||
bxls lr
|
||||
neg r0, r0
|
||||
b __set_errno_internal
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -67,3 +67,4 @@ __asm__ (
|
||||
|
||||
#include "../../arch-common/bionic/__dso_handle.h"
|
||||
#include "../../arch-common/bionic/atexit.h"
|
||||
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
mov x8, __NR_close
|
||||
svc #0
|
||||
|
||||
@@ -11,4 +11,5 @@ ENTRY(close)
|
||||
b.hi __set_errno_internal
|
||||
|
||||
ret
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -59,6 +59,7 @@ void _start() {
|
||||
|
||||
#include "__dso_handle.h"
|
||||
#include "atexit.h"
|
||||
#include "pthread_atfork.h"
|
||||
#ifdef __i386__
|
||||
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,7 @@ void __on_dlclose() {
|
||||
# include "__dso_handle_so.h"
|
||||
# include "atexit.h"
|
||||
#endif
|
||||
#include "pthread_atfork.h"
|
||||
#ifdef __i386__
|
||||
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
||||
#endif
|
||||
|
||||
29
libc/arch-common/bionic/pthread_atfork.h
Normal file
29
libc/arch-common/bionic/pthread_atfork.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern void* __dso_handle;
|
||||
|
||||
extern int __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void), void* dso);
|
||||
|
||||
#ifndef _LIBC
|
||||
// Libc used to export this in previous versions, therefore it needs
|
||||
// to remain global for binary compatibility.
|
||||
__attribute__ ((visibility ("hidden")))
|
||||
#endif
|
||||
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
|
||||
return __register_atfork(prepare, parent, child, &__dso_handle);
|
||||
}
|
||||
|
||||
@@ -92,3 +92,4 @@ __asm__ (
|
||||
|
||||
#include "../../arch-common/bionic/__dso_handle.h"
|
||||
#include "../../arch-common/bionic/atexit.h"
|
||||
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
.set noreorder
|
||||
.cpload t9
|
||||
li v0, __NR_close
|
||||
@@ -16,4 +16,5 @@ ENTRY(close)
|
||||
j t9
|
||||
nop
|
||||
.set reorder
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -92,3 +92,4 @@ __asm__ (
|
||||
|
||||
#include "../../arch-common/bionic/__dso_handle.h"
|
||||
#include "../../arch-common/bionic/atexit.h"
|
||||
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
.set push
|
||||
.set noreorder
|
||||
li v0, __NR_close
|
||||
@@ -22,4 +22,5 @@ ENTRY(close)
|
||||
j t9
|
||||
move ra, t0
|
||||
.set pop
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -3,8 +3,14 @@
|
||||
// pid_t __bionic_clone(int flags, void* child_stack, pid_t* parent_tid, void* tls, pid_t* child_tid, int (*fn)(void*), void* arg);
|
||||
ENTRY(__bionic_clone)
|
||||
pushl %ebx
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset ebx, 0
|
||||
pushl %esi
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset esi, 0
|
||||
pushl %edi
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset edi, 0
|
||||
|
||||
# Load system call arguments into registers.
|
||||
movl 16(%esp), %ebx # flags
|
||||
@@ -46,8 +52,14 @@ ENTRY(__bionic_clone)
|
||||
# We're the parent; nothing to do.
|
||||
.L_bc_return:
|
||||
popl %edi
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore edi
|
||||
popl %esi
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore esi
|
||||
popl %ebx
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore ebx
|
||||
ret
|
||||
END(__bionic_clone)
|
||||
.hidden __bionic_clone
|
||||
|
||||
@@ -15,9 +15,17 @@
|
||||
ENTRY(syscall)
|
||||
# Push the callee save registers.
|
||||
push %ebx
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset ebx, 0
|
||||
push %esi
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset esi, 0
|
||||
push %edi
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset edi, 0
|
||||
push %ebp
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset ebp, 0
|
||||
|
||||
# Load all the arguments from the calling frame.
|
||||
# (Not all will be valid, depending on the syscall.)
|
||||
@@ -43,8 +51,16 @@ ENTRY(syscall)
|
||||
1:
|
||||
# Restore the callee save registers.
|
||||
pop %ebp
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore ebp
|
||||
pop %edi
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore edi
|
||||
pop %esi
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore esi
|
||||
pop %ebx
|
||||
.cfi_adjust_cfa_offset -4
|
||||
.cfi_restore ebx
|
||||
ret
|
||||
END(syscall)
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
ENTRY(vfork)
|
||||
popl %ecx // Grab the return address.
|
||||
.cfi_adjust_cfa_offset 4
|
||||
.cfi_rel_offset ecx, 0
|
||||
movl $__NR_vfork, %eax
|
||||
int $0x80
|
||||
cmpl $-MAX_ERRNO, %eax
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
pushl %ebx
|
||||
.cfi_def_cfa_offset 8
|
||||
.cfi_rel_offset ebx, 0
|
||||
@@ -18,4 +18,5 @@ ENTRY(close)
|
||||
1:
|
||||
popl %ebx
|
||||
ret
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -1897,8 +1897,8 @@ L(strcmp_exitz):
|
||||
|
||||
.p2align 4
|
||||
L(Byte0):
|
||||
movzx (%rsi), %ecx
|
||||
movzx (%rdi), %eax
|
||||
movzbl (%rsi), %ecx
|
||||
movzbl (%rdi), %eax
|
||||
|
||||
sub %ecx, %eax
|
||||
ret
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <private/bionic_asm.h>
|
||||
|
||||
ENTRY(close)
|
||||
ENTRY(___close)
|
||||
movl $__NR_close, %eax
|
||||
syscall
|
||||
cmpq $-MAX_ERRNO, %rax
|
||||
@@ -12,4 +12,5 @@ ENTRY(close)
|
||||
call __set_errno_internal
|
||||
1:
|
||||
ret
|
||||
END(close)
|
||||
END(___close)
|
||||
.hidden ___close
|
||||
@@ -22,7 +22,7 @@ struct thread_local_dtor {
|
||||
thread_local_dtor* next;
|
||||
};
|
||||
|
||||
__thread thread_local_dtor* thread_local_dtors = nullptr;
|
||||
static __thread thread_local_dtor* thread_local_dtors = nullptr;
|
||||
|
||||
extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
|
||||
thread_local_dtor* dtor = new thread_local_dtor();
|
||||
|
||||
39
libc/bionic/__memchr_chk.cpp
Normal file
39
libc/bionic/__memchr_chk.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <string.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" void* __memchr_chk(const void* s, int c, size_t n, size_t buf_size) {
|
||||
if (__predict_false(n > buf_size)) {
|
||||
__fortify_chk_fail("memchr: prevented read past end of buffer", 0);
|
||||
}
|
||||
|
||||
return memchr(s, c, n);
|
||||
}
|
||||
39
libc/bionic/__memrchr_chk.cpp
Normal file
39
libc/bionic/__memrchr_chk.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <string.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" void* __memrchr_chk(const void* s, int c, size_t n, size_t buf_size) {
|
||||
if (__predict_false(n > buf_size)) {
|
||||
__fortify_chk_fail("memrchr: prevented read past end of buffer", 0);
|
||||
}
|
||||
|
||||
return memrchr(s, c, n);
|
||||
}
|
||||
43
libc/bionic/__pread64_chk.cpp
Normal file
43
libc/bionic/__pread64_chk.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <unistd.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" ssize_t __pread64_chk(int fd, void* buf, size_t count, off64_t offset, size_t buf_size) {
|
||||
if (__predict_false(count > buf_size)) {
|
||||
__fortify_chk_fail("pread64: prevented write past end of buffer", 0);
|
||||
}
|
||||
|
||||
if (__predict_false(count > SSIZE_MAX)) {
|
||||
__fortify_chk_fail("pread64: count > SSIZE_MAX", 0);
|
||||
}
|
||||
|
||||
return pread64(fd, buf, count, offset);
|
||||
}
|
||||
43
libc/bionic/__pread_chk.cpp
Normal file
43
libc/bionic/__pread_chk.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <unistd.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" ssize_t __pread_chk(int fd, void* buf, size_t count, off_t offset, size_t buf_size) {
|
||||
if (__predict_false(count > buf_size)) {
|
||||
__fortify_chk_fail("pread: prevented write past end of buffer", 0);
|
||||
}
|
||||
|
||||
if (__predict_false(count > SSIZE_MAX)) {
|
||||
__fortify_chk_fail("pread: count > SSIZE_MAX", 0);
|
||||
}
|
||||
|
||||
return pread(fd, buf, count, offset);
|
||||
}
|
||||
43
libc/bionic/__readlink_chk.cpp
Normal file
43
libc/bionic/__readlink_chk.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <unistd.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" ssize_t __readlink_chk(const char* path, char* buf, size_t size, size_t buf_size) {
|
||||
if (__predict_false(size > buf_size)) {
|
||||
__fortify_chk_fail("readlink: prevented write past end of buffer", 0);
|
||||
}
|
||||
|
||||
if (__predict_false(size > SSIZE_MAX)) {
|
||||
__fortify_chk_fail("readlink: size > SSIZE_MAX", 0);
|
||||
}
|
||||
|
||||
return readlink(path, buf, size);
|
||||
}
|
||||
43
libc/bionic/__readlinkat_chk.cpp
Normal file
43
libc/bionic/__readlinkat_chk.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
#include <unistd.h>
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
extern "C" ssize_t __readlinkat_chk(int dirfd, const char* path, char* buf, size_t size, size_t buf_size) {
|
||||
if (__predict_false(size > buf_size)) {
|
||||
__fortify_chk_fail("readlinkat: prevented write past end of buffer", 0);
|
||||
}
|
||||
|
||||
if (__predict_false(size > SSIZE_MAX)) {
|
||||
__fortify_chk_fail("readlinkat: size > SSIZE_MAX", 0);
|
||||
}
|
||||
|
||||
return readlinkat(dirfd, path, buf, size);
|
||||
}
|
||||
56
libc/bionic/close.cpp
Normal file
56
libc/bionic/close.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" int ___close(int);
|
||||
|
||||
int close(int fd) {
|
||||
int rc = ___close(fd);
|
||||
if (rc == -1 && errno == EINTR) {
|
||||
// POSIX says that if close returns with EINTR, the fd must not be closed.
|
||||
// Linus disagrees: http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
|
||||
// The future POSIX solution is posix_close (http://austingroupbugs.net/view.php?id=529),
|
||||
// with the state after EINTR being undefined, and EINPROGRESS for the case where close
|
||||
// was interrupted by a signal but the file descriptor was actually closed.
|
||||
// My concern with that future behavior is that it breaks existing code that assumes
|
||||
// that close only returns -1 if it failed. Unlike other system calls, I have real
|
||||
// difficulty even imagining a caller that would need to know that close was interrupted
|
||||
// but succeeded. So returning EINTR is wrong (because Linux always closes) and EINPROGRESS
|
||||
// is harmful because callers need to be rewritten to understand that EINPROGRESS isn't
|
||||
// actually a failure, but will be reported as one.
|
||||
|
||||
// We don't restore errno because that would incur a cost (the TLS read) for every caller.
|
||||
// Since callers don't know ahead of time whether close will legitimately fail, they need
|
||||
// to have stashed the old errno value anyway if they plan on using it afterwards, so
|
||||
// us clobbering errno here doesn't change anything in that respect.
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -56,6 +56,7 @@ enum AndroidEventLogType {
|
||||
EVENT_TYPE_LONG = 1,
|
||||
EVENT_TYPE_STRING = 2,
|
||||
EVENT_TYPE_LIST = 3,
|
||||
EVENT_TYPE_FLOAT = 4,
|
||||
};
|
||||
|
||||
struct BufferOutputStream {
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "pthread_internal.h"
|
||||
#include "private/bionic_futex.h"
|
||||
#include "private/kernel_sigset_t.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
// System calls.
|
||||
extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
|
||||
@@ -59,11 +60,11 @@ struct PosixTimer {
|
||||
|
||||
int sigev_notify;
|
||||
|
||||
// These fields are only needed for a SIGEV_THREAD timer.
|
||||
// The fields below are only needed for a SIGEV_THREAD timer.
|
||||
pthread_t callback_thread;
|
||||
void (*callback)(sigval_t);
|
||||
sigval_t callback_argument;
|
||||
volatile bool armed;
|
||||
atomic_bool deleted; // Set when the timer is deleted, to prevent further calling of callback.
|
||||
};
|
||||
|
||||
static __kernel_timer_t to_kernel_timer_id(timer_t timer) {
|
||||
@@ -85,8 +86,13 @@ static void* __timer_thread_start(void* arg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (si.si_code == SI_TIMER && timer->armed) {
|
||||
if (si.si_code == SI_TIMER) {
|
||||
// This signal was sent because a timer fired, so call the callback.
|
||||
|
||||
// All events to the callback will be ignored when the timer is deleted.
|
||||
if (atomic_load(&timer->deleted) == true) {
|
||||
continue;
|
||||
}
|
||||
timer->callback(timer->callback_argument);
|
||||
} else if (si.si_code == SI_TKILL) {
|
||||
// This signal was sent because someone wants us to exit.
|
||||
@@ -97,9 +103,7 @@ static void* __timer_thread_start(void* arg) {
|
||||
}
|
||||
|
||||
static void __timer_thread_stop(PosixTimer* timer) {
|
||||
// Immediately mark the timer as disarmed so even if some events
|
||||
// continue to happen, the callback won't be called.
|
||||
timer->armed = false;
|
||||
atomic_store(&timer->deleted, true);
|
||||
pthread_kill(timer->callback_thread, TIMER_SIGNAL);
|
||||
}
|
||||
|
||||
@@ -126,7 +130,7 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
|
||||
// Otherwise, this must be SIGEV_THREAD timer...
|
||||
timer->callback = evp->sigev_notify_function;
|
||||
timer->callback_argument = evp->sigev_value;
|
||||
timer->armed = false;
|
||||
atomic_init(&timer->deleted, false);
|
||||
|
||||
// Check arguments that the kernel doesn't care about but we do.
|
||||
if (timer->callback == NULL) {
|
||||
@@ -170,10 +174,10 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Give the thread a meaningful name.
|
||||
// Give the thread a specific meaningful name.
|
||||
// It can't do this itself because the kernel timer isn't created until after it's running.
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "POSIX interval timer %d", to_kernel_timer_id(timer));
|
||||
char name[16]; // 16 is the kernel-imposed limit.
|
||||
snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer));
|
||||
pthread_setname_np(timer->callback_thread, name);
|
||||
|
||||
*timer_id = timer;
|
||||
@@ -199,25 +203,19 @@ int timer_delete(timer_t id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_gettime.html
|
||||
int timer_gettime(timer_t id, itimerspec* ts) {
|
||||
return __timer_gettime(to_kernel_timer_id(id), ts);
|
||||
}
|
||||
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_settime.html
|
||||
// When using timer_settime to disarm a repeatable SIGEV_THREAD timer with a very small
|
||||
// period (like below 1ms), the kernel may continue to send events to the callback thread
|
||||
// for a few extra times. This behavior is fine because in POSIX standard: The effect of
|
||||
// disarming or resetting a timer with pending expiration notifications is unspecified.
|
||||
int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
|
||||
PosixTimer* timer= reinterpret_cast<PosixTimer*>(id);
|
||||
int rc = __timer_settime(timer->kernel_timer_id, flags, ts, ots);
|
||||
if (rc == 0) {
|
||||
// Mark the timer as either being armed or disarmed. This avoids the
|
||||
// callback being called after the disarm for SIGEV_THREAD timers only.
|
||||
if (ts->it_value.tv_sec != 0 || ts->it_value.tv_nsec != 0) {
|
||||
timer->armed = true;
|
||||
} else {
|
||||
timer->armed = false;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
return __timer_settime(timer->kernel_timer_id, flags, ts, ots);
|
||||
}
|
||||
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "private/bionic_macros.h"
|
||||
|
||||
struct atfork_t {
|
||||
atfork_t* next;
|
||||
atfork_t* prev;
|
||||
@@ -37,79 +39,143 @@ struct atfork_t {
|
||||
void (*prepare)(void);
|
||||
void (*child)(void);
|
||||
void (*parent)(void);
|
||||
|
||||
void* dso_handle;
|
||||
};
|
||||
|
||||
struct atfork_list_t {
|
||||
atfork_t* first;
|
||||
atfork_t* last;
|
||||
class atfork_list_t {
|
||||
public:
|
||||
atfork_list_t() : first_(nullptr), last_(nullptr) {}
|
||||
|
||||
template<typename F>
|
||||
void walk_forward(F f) {
|
||||
for (atfork_t* it = first_; it != nullptr; it = it->next) {
|
||||
f(it);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void walk_backwards(F f) {
|
||||
for (atfork_t* it = last_; it != nullptr; it = it->prev) {
|
||||
f(it);
|
||||
}
|
||||
}
|
||||
|
||||
void push_back(atfork_t* entry) {
|
||||
entry->next = nullptr;
|
||||
entry->prev = last_;
|
||||
if (entry->prev != nullptr) {
|
||||
entry->prev->next = entry;
|
||||
}
|
||||
if (first_ == nullptr) {
|
||||
first_ = entry;
|
||||
}
|
||||
last_ = entry;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void remove_if(F predicate) {
|
||||
atfork_t* it = first_;
|
||||
while (it != nullptr) {
|
||||
if (predicate(it)) {
|
||||
atfork_t* entry = it;
|
||||
it = it->next;
|
||||
remove(entry);
|
||||
} else {
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void remove(atfork_t* entry) {
|
||||
if (entry->prev != nullptr) {
|
||||
entry->prev->next = entry->next;
|
||||
} else {
|
||||
first_ = entry->next;
|
||||
}
|
||||
|
||||
if (entry->next != nullptr) {
|
||||
entry->next->prev = entry->prev;
|
||||
} else {
|
||||
last_ = entry->prev;
|
||||
}
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
atfork_t* first_;
|
||||
atfork_t* last_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(atfork_list_t);
|
||||
};
|
||||
|
||||
static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
static atfork_list_t g_atfork_list = { NULL, NULL };
|
||||
static atfork_list_t g_atfork_list;
|
||||
|
||||
void __bionic_atfork_run_prepare() {
|
||||
// We lock the atfork list here, unlock it in the parent, and reset it in the child.
|
||||
// This ensures that nobody can modify the handler array between the calls
|
||||
// to the prepare and parent/child handlers.
|
||||
//
|
||||
// TODO: If a handler tries to mutate the list, they'll block. We should probably copy
|
||||
// the list before forking, and have prepare, parent, and child all work on the consistent copy.
|
||||
pthread_mutex_lock(&g_atfork_list_mutex);
|
||||
|
||||
// Call pthread_atfork() prepare handlers. POSIX states that the prepare
|
||||
// handlers should be called in the reverse order of the parent/child
|
||||
// handlers, so we iterate backwards.
|
||||
for (atfork_t* it = g_atfork_list.last; it != NULL; it = it->prev) {
|
||||
if (it->prepare != NULL) {
|
||||
g_atfork_list.walk_backwards([](atfork_t* it) {
|
||||
if (it->prepare != nullptr) {
|
||||
it->prepare();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void __bionic_atfork_run_child() {
|
||||
for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) {
|
||||
if (it->child != NULL) {
|
||||
g_atfork_list.walk_forward([](atfork_t* it) {
|
||||
if (it->child != nullptr) {
|
||||
it->child();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
}
|
||||
|
||||
void __bionic_atfork_run_parent() {
|
||||
for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) {
|
||||
if (it->parent != NULL) {
|
||||
g_atfork_list.walk_forward([](atfork_t* it) {
|
||||
if (it->parent != nullptr) {
|
||||
it->parent();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pthread_mutex_unlock(&g_atfork_list_mutex);
|
||||
}
|
||||
|
||||
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void)) {
|
||||
// __register_atfork is the name used by glibc
|
||||
extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void),
|
||||
void(*child)(void), void* dso) {
|
||||
atfork_t* entry = reinterpret_cast<atfork_t*>(malloc(sizeof(atfork_t)));
|
||||
if (entry == NULL) {
|
||||
if (entry == nullptr) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
entry->prepare = prepare;
|
||||
entry->parent = parent;
|
||||
entry->child = child;
|
||||
entry->dso_handle = dso;
|
||||
|
||||
pthread_mutex_lock(&g_atfork_list_mutex);
|
||||
|
||||
// Append 'entry' to the list.
|
||||
entry->next = NULL;
|
||||
entry->prev = g_atfork_list.last;
|
||||
if (entry->prev != NULL) {
|
||||
entry->prev->next = entry;
|
||||
}
|
||||
if (g_atfork_list.first == NULL) {
|
||||
g_atfork_list.first = entry;
|
||||
}
|
||||
g_atfork_list.last = entry;
|
||||
g_atfork_list.push_back(entry);
|
||||
|
||||
pthread_mutex_unlock(&g_atfork_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) {
|
||||
pthread_mutex_lock(&g_atfork_list_mutex);
|
||||
g_atfork_list.remove_if([&](const atfork_t* entry) {
|
||||
return entry->dso_handle == dso;
|
||||
});
|
||||
pthread_mutex_unlock(&g_atfork_list_mutex);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,15 @@ static inline bool SeqOfKeyInUse(uintptr_t seq) {
|
||||
return seq & (1 << SEQ_KEY_IN_USE_BIT);
|
||||
}
|
||||
|
||||
#define KEY_VALID_FLAG (1 << 31)
|
||||
|
||||
static_assert(sizeof(pthread_key_t) == sizeof(int) && static_cast<pthread_key_t>(-1) < 0,
|
||||
"pthread_key_t should be typedef to int");
|
||||
|
||||
static inline bool KeyInValidRange(pthread_key_t key) {
|
||||
return key >= 0 && key < BIONIC_PTHREAD_KEY_COUNT;
|
||||
// key < 0 means bit 31 is set.
|
||||
// Then key < (2^31 | BIONIC_PTHREAD_KEY_COUNT) means the index part of key < BIONIC_PTHREAD_KEY_COUNT.
|
||||
return (key < (KEY_VALID_FLAG | BIONIC_PTHREAD_KEY_COUNT));
|
||||
}
|
||||
|
||||
// Called from pthread_exit() to remove all pthread keys. This must call the destructor of
|
||||
@@ -114,7 +121,7 @@ int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
||||
while (!SeqOfKeyInUse(seq)) {
|
||||
if (atomic_compare_exchange_weak(&key_map[i].seq, &seq, seq + SEQ_INCREMENT_STEP)) {
|
||||
atomic_store(&key_map[i].key_destructor, reinterpret_cast<uintptr_t>(key_destructor));
|
||||
*key = i;
|
||||
*key = i | KEY_VALID_FLAG;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -127,9 +134,10 @@ int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
|
||||
// responsibility of the caller to properly dispose of the corresponding data
|
||||
// and resources, using any means it finds suitable.
|
||||
int pthread_key_delete(pthread_key_t key) {
|
||||
if (!KeyInValidRange(key)) {
|
||||
if (__predict_false(!KeyInValidRange(key))) {
|
||||
return EINVAL;
|
||||
}
|
||||
key &= ~KEY_VALID_FLAG;
|
||||
// Increase seq to invalidate values in all threads.
|
||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||
if (SeqOfKeyInUse(seq)) {
|
||||
@@ -141,9 +149,10 @@ int pthread_key_delete(pthread_key_t key) {
|
||||
}
|
||||
|
||||
void* pthread_getspecific(pthread_key_t key) {
|
||||
if (!KeyInValidRange(key)) {
|
||||
if (__predict_false(!KeyInValidRange(key))) {
|
||||
return NULL;
|
||||
}
|
||||
key &= ~KEY_VALID_FLAG;
|
||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
||||
// It is user's responsibility to synchornize between the creation and use of pthread keys,
|
||||
@@ -151,16 +160,19 @@ void* pthread_getspecific(pthread_key_t key) {
|
||||
if (__predict_true(SeqOfKeyInUse(seq) && data->seq == seq)) {
|
||||
return data->data;
|
||||
}
|
||||
// We arrive here when current thread holds the seq of an deleted pthread key. So the
|
||||
// data is for the deleted pthread key, and should be cleared.
|
||||
data->data = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
||||
if (!KeyInValidRange(key)) {
|
||||
if (__predict_false(!KeyInValidRange(key))) {
|
||||
return EINVAL;
|
||||
}
|
||||
key &= ~KEY_VALID_FLAG;
|
||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||
if (SeqOfKeyInUse(seq)) {
|
||||
if (__predict_true(SeqOfKeyInUse(seq))) {
|
||||
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
||||
data->seq = seq;
|
||||
data->data = const_cast<void*>(ptr);
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pthread_internal.h"
|
||||
#include "private/bionic_futex.h"
|
||||
#include "private/bionic_lock.h"
|
||||
#include "private/bionic_time_conversions.h"
|
||||
|
||||
/* Technical note:
|
||||
@@ -53,18 +55,39 @@
|
||||
* - This implementation will return EDEADLK in "write after write" and "read after
|
||||
* write" cases and will deadlock in write after read case.
|
||||
*
|
||||
* TODO: As it stands now, pending_readers and pending_writers could be merged into a
|
||||
* a single waiters variable. Keeping them separate adds a bit of clarity and keeps
|
||||
* the door open for a writer-biased implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
#define RWLOCKATTR_DEFAULT 0
|
||||
#define RWLOCKATTR_SHARED_MASK 0x0010
|
||||
// A rwlockattr is implemented as a 32-bit integer which has following fields:
|
||||
// bits name description
|
||||
// 1 rwlock_kind have rwlock preference like PTHREAD_RWLOCK_PREFER_READER_NP.
|
||||
// 0 process_shared set to 1 if the rwlock is shared between processes.
|
||||
|
||||
#define RWLOCKATTR_PSHARED_SHIFT 0
|
||||
#define RWLOCKATTR_KIND_SHIFT 1
|
||||
|
||||
#define RWLOCKATTR_PSHARED_MASK 1
|
||||
#define RWLOCKATTR_KIND_MASK 2
|
||||
#define RWLOCKATTR_RESERVED_MASK (~3)
|
||||
|
||||
static inline __always_inline __always_inline bool __rwlockattr_getpshared(const pthread_rwlockattr_t* attr) {
|
||||
return (*attr & RWLOCKATTR_PSHARED_MASK) >> RWLOCKATTR_PSHARED_SHIFT;
|
||||
}
|
||||
|
||||
static inline __always_inline __always_inline void __rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared) {
|
||||
*attr = (*attr & ~RWLOCKATTR_PSHARED_MASK) | (pshared << RWLOCKATTR_PSHARED_SHIFT);
|
||||
}
|
||||
|
||||
static inline __always_inline int __rwlockattr_getkind(const pthread_rwlockattr_t* attr) {
|
||||
return (*attr & RWLOCKATTR_KIND_MASK) >> RWLOCKATTR_KIND_SHIFT;
|
||||
}
|
||||
|
||||
static inline __always_inline void __rwlockattr_setkind(pthread_rwlockattr_t* attr, int kind) {
|
||||
*attr = (*attr & ~RWLOCKATTR_KIND_MASK) | (kind << RWLOCKATTR_KIND_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr) {
|
||||
*attr = PTHREAD_PROCESS_PRIVATE;
|
||||
*attr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -73,40 +96,121 @@ int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) {
|
||||
if (__rwlockattr_getpshared(attr)) {
|
||||
*pshared = PTHREAD_PROCESS_SHARED;
|
||||
} else {
|
||||
*pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared) {
|
||||
switch (pshared) {
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
__rwlockattr_setpshared(attr, 0);
|
||||
return 0;
|
||||
case PTHREAD_PROCESS_SHARED:
|
||||
*attr = pshared;
|
||||
__rwlockattr_setpshared(attr, 1);
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) {
|
||||
*pshared = *attr;
|
||||
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t* attr, int* pref) {
|
||||
*pref = __rwlockattr_getkind(attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pthread_rwlock_internal_t {
|
||||
atomic_int state; // 0=unlock, -1=writer lock, +n=reader lock
|
||||
atomic_int writer_thread_id;
|
||||
atomic_uint pending_readers;
|
||||
atomic_uint pending_writers;
|
||||
int32_t attr;
|
||||
|
||||
bool process_shared() const {
|
||||
return attr == PTHREAD_PROCESS_SHARED;
|
||||
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref) {
|
||||
switch (pref) {
|
||||
case PTHREAD_RWLOCK_PREFER_READER_NP: // Fall through.
|
||||
case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
|
||||
__rwlockattr_setkind(attr, pref);
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
// A rwlock state is implemented as a 32-bit integer which has following rules:
|
||||
// bits name description
|
||||
// 31 owned_by_writer_flag set to 1 if the lock is owned by a writer now.
|
||||
// 30-2 reader_count the count of readers holding the lock.
|
||||
// 1 have_pending_writers set to 1 if having pending writers.
|
||||
// 0 have_pending_readers set to 1 if having pending readers.
|
||||
|
||||
#define STATE_HAVE_PENDING_READERS_SHIFT 0
|
||||
#define STATE_HAVE_PENDING_WRITERS_SHIFT 1
|
||||
#define STATE_READER_COUNT_SHIFT 2
|
||||
#define STATE_OWNED_BY_WRITER_SHIFT 31
|
||||
|
||||
#define STATE_HAVE_PENDING_READERS_FLAG (1 << STATE_HAVE_PENDING_READERS_SHIFT)
|
||||
#define STATE_HAVE_PENDING_WRITERS_FLAG (1 << STATE_HAVE_PENDING_WRITERS_SHIFT)
|
||||
#define STATE_READER_COUNT_CHANGE_STEP (1 << STATE_READER_COUNT_SHIFT)
|
||||
#define STATE_OWNED_BY_WRITER_FLAG (1 << STATE_OWNED_BY_WRITER_SHIFT)
|
||||
|
||||
#define STATE_HAVE_PENDING_READERS_OR_WRITERS_FLAG \
|
||||
(STATE_HAVE_PENDING_READERS_FLAG | STATE_HAVE_PENDING_WRITERS_FLAG)
|
||||
|
||||
struct pthread_rwlock_internal_t {
|
||||
atomic_int state;
|
||||
atomic_int writer_tid;
|
||||
|
||||
bool pshared;
|
||||
bool writer_nonrecursive_preferred;
|
||||
uint16_t __pad;
|
||||
|
||||
// When a reader thread plans to suspend on the rwlock, it will add STATE_HAVE_PENDING_READERS_FLAG
|
||||
// in state, increase pending_reader_count, and wait on pending_reader_wakeup_serial. After woken
|
||||
// up, the reader thread decreases pending_reader_count, and the last pending reader thread should
|
||||
// remove STATE_HAVE_PENDING_READERS_FLAG in state. A pending writer thread works in a similar way,
|
||||
// except that it uses flag and members for writer threads.
|
||||
|
||||
Lock pending_lock; // All pending members below are protected by pending_lock.
|
||||
uint32_t pending_reader_count; // Count of pending reader threads.
|
||||
uint32_t pending_writer_count; // Count of pending writer threads.
|
||||
uint32_t pending_reader_wakeup_serial; // Pending reader threads wait on this address by futex_wait.
|
||||
uint32_t pending_writer_wakeup_serial; // Pending writer threads wait on this address by futex_wait.
|
||||
|
||||
#if defined(__LP64__)
|
||||
char __reserved[36];
|
||||
#else
|
||||
char __reserved[20];
|
||||
#else
|
||||
char __reserved[4];
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline __always_inline bool __state_owned_by_writer(int state) {
|
||||
return state < 0;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __state_owned_by_readers(int state) {
|
||||
// If state >= 0, the owned_by_writer_flag is not set.
|
||||
// And if state >= STATE_READER_COUNT_CHANGE_STEP, the reader_count field is not empty.
|
||||
return state >= STATE_READER_COUNT_CHANGE_STEP;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __state_owned_by_readers_or_writer(int state) {
|
||||
return state < 0 || state >= STATE_READER_COUNT_CHANGE_STEP;
|
||||
}
|
||||
|
||||
static inline __always_inline int __state_add_writer_flag(int state) {
|
||||
return state | STATE_OWNED_BY_WRITER_FLAG;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __state_is_last_reader(int state) {
|
||||
return (state >> STATE_READER_COUNT_SHIFT) == 1;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __state_have_pending_writers(int state) {
|
||||
return state & STATE_HAVE_PENDING_WRITERS_FLAG;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __state_have_pending_readers_or_writers(int state) {
|
||||
return state & STATE_HAVE_PENDING_READERS_OR_WRITERS_FLAG;
|
||||
}
|
||||
|
||||
static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t),
|
||||
"pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation.");
|
||||
|
||||
@@ -115,31 +219,35 @@ static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t),
|
||||
static_assert(alignof(pthread_rwlock_t) == 4,
|
||||
"pthread_rwlock_t should fulfill the alignment requirement of pthread_rwlock_internal_t.");
|
||||
|
||||
static inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) {
|
||||
static inline __always_inline pthread_rwlock_internal_t* __get_internal_rwlock(pthread_rwlock_t* rwlock_interface) {
|
||||
return reinterpret_cast<pthread_rwlock_internal_t*>(rwlock_interface);
|
||||
}
|
||||
|
||||
int pthread_rwlock_init(pthread_rwlock_t* rwlock_interface, const pthread_rwlockattr_t* attr) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
if (__predict_true(attr == NULL)) {
|
||||
rwlock->attr = 0;
|
||||
} else {
|
||||
switch (*attr) {
|
||||
case PTHREAD_PROCESS_SHARED:
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
rwlock->attr= *attr;
|
||||
memset(rwlock, 0, sizeof(pthread_rwlock_internal_t));
|
||||
|
||||
if (__predict_false(attr != NULL)) {
|
||||
rwlock->pshared = __rwlockattr_getpshared(attr);
|
||||
int kind = __rwlockattr_getkind(attr);
|
||||
switch (kind) {
|
||||
case PTHREAD_RWLOCK_PREFER_READER_NP:
|
||||
rwlock->writer_nonrecursive_preferred = false;
|
||||
break;
|
||||
case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
|
||||
rwlock->writer_nonrecursive_preferred = true;
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
if ((*attr & RWLOCKATTR_RESERVED_MASK) != 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_init(&rwlock->state, 0);
|
||||
atomic_init(&rwlock->writer_thread_id, 0);
|
||||
atomic_init(&rwlock->pending_readers, 0);
|
||||
atomic_init(&rwlock->pending_writers, 0);
|
||||
|
||||
rwlock->pending_lock.init(rwlock->pshared);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -152,105 +260,173 @@ int pthread_rwlock_destroy(pthread_rwlock_t* rwlock_interface) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline __always_inline bool __can_acquire_read_lock(int old_state,
|
||||
bool writer_nonrecursive_preferred) {
|
||||
// If writer is preferred with nonrecursive reader, we prevent further readers from acquiring
|
||||
// the lock when there are writers waiting for the lock.
|
||||
bool cannot_apply = __state_owned_by_writer(old_state) ||
|
||||
(writer_nonrecursive_preferred && __state_have_pending_writers(old_state));
|
||||
return !cannot_apply;
|
||||
}
|
||||
|
||||
static inline __always_inline int __pthread_rwlock_tryrdlock(pthread_rwlock_internal_t* rwlock) {
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
|
||||
while (__predict_true(__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred))) {
|
||||
|
||||
int new_state = old_state + STATE_READER_COUNT_CHANGE_STEP;
|
||||
if (__predict_false(!__state_owned_by_readers(new_state))) { // Happens when reader count overflows.
|
||||
return EAGAIN;
|
||||
}
|
||||
if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, new_state,
|
||||
memory_order_acquire, memory_order_relaxed))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
||||
const timespec* abs_timeout_or_null) {
|
||||
|
||||
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
||||
memory_order_relaxed))) {
|
||||
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) {
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int ret = __pthread_rwlock_tryrdlock(rwlock);
|
||||
if (ret == 0 || ret == EAGAIN) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
if (__predict_true(old_state >= 0)) {
|
||||
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, old_state + 1,
|
||||
memory_order_acquire, memory_order_relaxed)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
timespec ts;
|
||||
timespec* rel_timeout = NULL;
|
||||
if (__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (abs_timeout_or_null != NULL) {
|
||||
rel_timeout = &ts;
|
||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
timespec ts;
|
||||
timespec* rel_timeout = NULL;
|
||||
|
||||
// To avoid losing wake ups, the pending_readers increment should be observed before
|
||||
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
||||
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
||||
// operations in futex_wait.
|
||||
atomic_fetch_add_explicit(&rwlock->pending_readers, 1, memory_order_relaxed);
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state,
|
||||
rel_timeout);
|
||||
|
||||
atomic_fetch_sub_explicit(&rwlock->pending_readers, 1, memory_order_relaxed);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
if (abs_timeout_or_null != NULL) {
|
||||
rel_timeout = &ts;
|
||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
rwlock->pending_lock.lock();
|
||||
rwlock->pending_reader_count++;
|
||||
|
||||
// We rely on the fact that all atomic exchange operations on the same object (here it is
|
||||
// rwlock->state) always appear to occur in a single total order. If the pending flag is added
|
||||
// before unlocking, the unlocking thread will wakeup the waiter. Otherwise, we will see the
|
||||
// state is unlocked and will not wait anymore.
|
||||
old_state = atomic_fetch_or_explicit(&rwlock->state, STATE_HAVE_PENDING_READERS_FLAG,
|
||||
memory_order_relaxed);
|
||||
|
||||
int old_serial = rwlock->pending_reader_wakeup_serial;
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
int futex_ret = 0;
|
||||
if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
|
||||
futex_ret = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
|
||||
old_serial, rel_timeout);
|
||||
}
|
||||
|
||||
rwlock->pending_lock.lock();
|
||||
rwlock->pending_reader_count--;
|
||||
if (rwlock->pending_reader_count == 0) {
|
||||
atomic_fetch_and_explicit(&rwlock->state, ~STATE_HAVE_PENDING_READERS_FLAG,
|
||||
memory_order_relaxed);
|
||||
}
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
if (futex_ret == -ETIMEDOUT) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline __always_inline bool __can_acquire_write_lock(int old_state) {
|
||||
return !__state_owned_by_readers_or_writer(old_state);
|
||||
}
|
||||
|
||||
static inline __always_inline int __pthread_rwlock_trywrlock(pthread_rwlock_internal_t* rwlock) {
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
|
||||
while (__predict_true(__can_acquire_write_lock(old_state))) {
|
||||
if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
|
||||
__state_add_writer_flag(old_state), memory_order_acquire, memory_order_relaxed))) {
|
||||
|
||||
atomic_store_explicit(&rwlock->writer_tid, __get_thread()->tid, memory_order_relaxed);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
||||
const timespec* abs_timeout_or_null) {
|
||||
|
||||
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
||||
memory_order_relaxed))) {
|
||||
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) {
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int ret = __pthread_rwlock_trywrlock(rwlock);
|
||||
if (ret == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
if (__predict_true(old_state == 0)) {
|
||||
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1,
|
||||
memory_order_acquire, memory_order_relaxed)) {
|
||||
// writer_thread_id is protected by rwlock and can only be modified in rwlock write
|
||||
// owner thread. Other threads may read it for EDEADLK error checking, atomic operation
|
||||
// is safe enough for it.
|
||||
atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
timespec ts;
|
||||
timespec* rel_timeout = NULL;
|
||||
if (__can_acquire_write_lock(old_state)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (abs_timeout_or_null != NULL) {
|
||||
rel_timeout = &ts;
|
||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
timespec ts;
|
||||
timespec* rel_timeout = NULL;
|
||||
|
||||
// To avoid losing wake ups, the pending_writers increment should be observed before
|
||||
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
||||
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
||||
// operations in futex_wait.
|
||||
atomic_fetch_add_explicit(&rwlock->pending_writers, 1, memory_order_relaxed);
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
int ret = __futex_wait_ex(&rwlock->state, rwlock->process_shared(), old_state,
|
||||
rel_timeout);
|
||||
|
||||
atomic_fetch_sub_explicit(&rwlock->pending_writers, 1, memory_order_relaxed);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
if (abs_timeout_or_null != NULL) {
|
||||
rel_timeout = &ts;
|
||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
rwlock->pending_lock.lock();
|
||||
rwlock->pending_writer_count++;
|
||||
|
||||
old_state = atomic_fetch_or_explicit(&rwlock->state, STATE_HAVE_PENDING_WRITERS_FLAG,
|
||||
memory_order_relaxed);
|
||||
|
||||
int old_serial = rwlock->pending_writer_wakeup_serial;
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
int futex_ret = 0;
|
||||
if (!__can_acquire_write_lock(old_state)) {
|
||||
futex_ret = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared,
|
||||
old_serial, rel_timeout);
|
||||
}
|
||||
|
||||
rwlock->pending_lock.lock();
|
||||
rwlock->pending_writer_count--;
|
||||
if (rwlock->pending_writer_count == 0) {
|
||||
atomic_fetch_and_explicit(&rwlock->state, ~STATE_HAVE_PENDING_WRITERS_FLAG,
|
||||
memory_order_relaxed);
|
||||
}
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
if (futex_ret == -ETIMEDOUT) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_interface) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
// Avoid slowing down fast path of rdlock.
|
||||
if (__predict_true(__pthread_rwlock_tryrdlock(rwlock) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
return __pthread_rwlock_timedrdlock(rwlock, NULL);
|
||||
}
|
||||
|
||||
@@ -261,19 +437,15 @@ int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespe
|
||||
}
|
||||
|
||||
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock_interface) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
|
||||
while (old_state >= 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
|
||||
old_state + 1, memory_order_acquire, memory_order_relaxed)) {
|
||||
}
|
||||
return (old_state >= 0) ? 0 : EBUSY;
|
||||
return __pthread_rwlock_tryrdlock(__get_internal_rwlock(rwlock_interface));
|
||||
}
|
||||
|
||||
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
// Avoid slowing down fast path of wrlock.
|
||||
if (__predict_true(__pthread_rwlock_trywrlock(rwlock) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
return __pthread_rwlock_timedwrlock(rwlock, NULL);
|
||||
}
|
||||
|
||||
@@ -284,65 +456,52 @@ int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespe
|
||||
}
|
||||
|
||||
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock_interface) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
|
||||
while (old_state == 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1,
|
||||
memory_order_acquire, memory_order_relaxed)) {
|
||||
}
|
||||
if (old_state == 0) {
|
||||
atomic_store_explicit(&rwlock->writer_thread_id, __get_thread()->tid, memory_order_relaxed);
|
||||
return 0;
|
||||
}
|
||||
return EBUSY;
|
||||
return __pthread_rwlock_trywrlock(__get_internal_rwlock(rwlock_interface));
|
||||
}
|
||||
|
||||
|
||||
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_interface) {
|
||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||
|
||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||
if (__predict_false(old_state == 0)) {
|
||||
return EPERM;
|
||||
} else if (old_state == -1) {
|
||||
if (atomic_load_explicit(&rwlock->writer_thread_id, memory_order_relaxed) != __get_thread()->tid) {
|
||||
if (__state_owned_by_writer(old_state)) {
|
||||
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) != __get_thread()->tid) {
|
||||
return EPERM;
|
||||
}
|
||||
// We're no longer the owner.
|
||||
atomic_store_explicit(&rwlock->writer_thread_id, 0, memory_order_relaxed);
|
||||
// Change state from -1 to 0.
|
||||
atomic_store_explicit(&rwlock->state, 0, memory_order_release);
|
||||
|
||||
} else { // old_state > 0
|
||||
// Reduce state by 1.
|
||||
while (old_state > 0 && !atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
|
||||
old_state - 1, memory_order_release, memory_order_relaxed)) {
|
||||
}
|
||||
|
||||
if (old_state <= 0) {
|
||||
return EPERM;
|
||||
} else if (old_state > 1) {
|
||||
atomic_store_explicit(&rwlock->writer_tid, 0, memory_order_relaxed);
|
||||
old_state = atomic_fetch_and_explicit(&rwlock->state, ~STATE_OWNED_BY_WRITER_FLAG,
|
||||
memory_order_release);
|
||||
if (!__state_have_pending_readers_or_writers(old_state)) {
|
||||
return 0;
|
||||
}
|
||||
// old_state = 1, which means the last reader calling unlock. It has to wake up waiters.
|
||||
|
||||
} else if (__state_owned_by_readers(old_state)) {
|
||||
old_state = atomic_fetch_sub_explicit(&rwlock->state, STATE_READER_COUNT_CHANGE_STEP,
|
||||
memory_order_release);
|
||||
if (!__state_is_last_reader(old_state) || !__state_have_pending_readers_or_writers(old_state)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
// If having waiters, wake up them.
|
||||
// To avoid losing wake ups, the update of state should be observed before reading
|
||||
// pending_readers/pending_writers by all threads. Use read locking as an example:
|
||||
// read locking thread unlocking thread
|
||||
// pending_readers++; state = 0;
|
||||
// seq_cst fence seq_cst fence
|
||||
// read state for futex_wait read pending_readers for futex_wake
|
||||
//
|
||||
// So when locking and unlocking threads are running in parallel, we will not get
|
||||
// in a situation that the locking thread reads state as negative and needs to wait,
|
||||
// while the unlocking thread reads pending_readers as zero and doesn't need to wake up waiters.
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
if (__predict_false(atomic_load_explicit(&rwlock->pending_readers, memory_order_relaxed) > 0 ||
|
||||
atomic_load_explicit(&rwlock->pending_writers, memory_order_relaxed) > 0)) {
|
||||
__futex_wake_ex(&rwlock->state, rwlock->process_shared(), INT_MAX);
|
||||
// Wake up pending readers or writers.
|
||||
rwlock->pending_lock.lock();
|
||||
if (rwlock->pending_writer_count != 0) {
|
||||
rwlock->pending_writer_wakeup_serial++;
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
__futex_wake_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared, 1);
|
||||
|
||||
} else if (rwlock->pending_reader_count != 0) {
|
||||
rwlock->pending_reader_wakeup_serial++;
|
||||
rwlock->pending_lock.unlock();
|
||||
|
||||
__futex_wake_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared, INT_MAX);
|
||||
|
||||
} else {
|
||||
// It happens when waiters are woken up by timeout.
|
||||
rwlock->pending_lock.unlock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -245,6 +245,7 @@ static id_t app_id_from_name(const char* name, bool is_group) {
|
||||
appid = android_ids[n].aid;
|
||||
// Move the end pointer to the null terminator.
|
||||
end += strlen(android_ids[n].name) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,6 +598,16 @@ int __system_property_area_init()
|
||||
return map_prop_area_rw();
|
||||
}
|
||||
|
||||
unsigned int __system_property_area_serial()
|
||||
{
|
||||
prop_area *pa = __system_property_area__;
|
||||
if (!pa) {
|
||||
return -1;
|
||||
}
|
||||
// Make sure this read fulfilled before __system_property_serial
|
||||
return atomic_load_explicit(&(pa->serial), memory_order_acquire);
|
||||
}
|
||||
|
||||
const prop_info *__system_property_find(const char *name)
|
||||
{
|
||||
if (__predict_false(compat_mode)) {
|
||||
|
||||
@@ -48,6 +48,71 @@ typedef struct {
|
||||
} a_un;
|
||||
} Elf64_auxv_t;
|
||||
|
||||
typedef Elf32_Half Elf32_Versym;
|
||||
typedef Elf64_Half Elf64_Versym;
|
||||
|
||||
typedef struct {
|
||||
Elf32_Half vd_version;
|
||||
Elf32_Half vd_flags;
|
||||
Elf32_Half vd_ndx;
|
||||
Elf32_Half vd_cnt;
|
||||
Elf32_Word vd_hash;
|
||||
Elf32_Word vd_aux;
|
||||
Elf32_Word vd_next;
|
||||
} Elf32_Verdef;
|
||||
|
||||
typedef struct {
|
||||
Elf32_Word vda_name;
|
||||
Elf32_Word vda_next;
|
||||
} Elf32_Verdaux;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half vd_version;
|
||||
Elf64_Half vd_flags;
|
||||
Elf64_Half vd_ndx;
|
||||
Elf64_Half vd_cnt;
|
||||
Elf64_Word vd_hash;
|
||||
Elf64_Word vd_aux;
|
||||
Elf64_Word vd_next;
|
||||
} Elf64_Verdef;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Word vda_name;
|
||||
Elf64_Word vda_next;
|
||||
} Elf64_Verdaux;
|
||||
|
||||
typedef struct {
|
||||
Elf32_Half vn_version;
|
||||
Elf32_Half vn_cnt;
|
||||
Elf32_Word vn_file;
|
||||
Elf32_Word vn_aux;
|
||||
Elf32_Word vn_next;
|
||||
} Elf32_Verneed;
|
||||
|
||||
typedef struct {
|
||||
Elf32_Word vna_hash;
|
||||
Elf32_Half vna_flags;
|
||||
Elf32_Half vna_other;
|
||||
Elf32_Word vna_name;
|
||||
Elf32_Word vna_next;
|
||||
} Elf32_Vernaux;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half vn_version;
|
||||
Elf64_Half vn_cnt;
|
||||
Elf64_Word vn_file;
|
||||
Elf64_Word vn_aux;
|
||||
Elf64_Word vn_next;
|
||||
} Elf64_Verneed;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Word vna_hash;
|
||||
Elf64_Half vna_flags;
|
||||
Elf64_Half vna_other;
|
||||
Elf64_Word vna_name;
|
||||
Elf64_Word vna_next;
|
||||
} Elf64_Vernaux;
|
||||
|
||||
#define DF_ORIGIN 0x00000001
|
||||
#define DF_SYMBOLIC 0x00000002
|
||||
#define DF_TEXTREL 0x00000004
|
||||
@@ -129,4 +194,10 @@ typedef struct {
|
||||
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
|
||||
#define VER_FLG_BASE 0x1
|
||||
#define VER_FLG_WEAK 0x2
|
||||
|
||||
#define VER_NDX_LOCAL 0
|
||||
#define VER_NDX_GLOBAL 1
|
||||
|
||||
#endif /* _ELF_H */
|
||||
|
||||
@@ -85,6 +85,11 @@ typedef long pthread_rwlockattr_t;
|
||||
|
||||
#define PTHREAD_RWLOCK_INITIALIZER { { 0 } }
|
||||
|
||||
enum {
|
||||
PTHREAD_RWLOCK_PREFER_READER_NP = 0,
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 1,
|
||||
};
|
||||
|
||||
typedef int pthread_key_t;
|
||||
|
||||
typedef int pthread_once_t;
|
||||
@@ -178,10 +183,12 @@ int pthread_mutex_unlock(pthread_mutex_t*) __nonnull((1));
|
||||
|
||||
int pthread_once(pthread_once_t*, void (*)(void)) __nonnull((1, 2));
|
||||
|
||||
int pthread_rwlockattr_init(pthread_rwlockattr_t*) __nonnull((1));
|
||||
int pthread_rwlockattr_destroy(pthread_rwlockattr_t*) __nonnull((1));
|
||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t*, int*) __nonnull((1, 2));
|
||||
int pthread_rwlockattr_init(pthread_rwlockattr_t*) __nonnull((1));
|
||||
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int) __nonnull((1));
|
||||
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t*, int*) __nonnull((1, 2));
|
||||
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t*, int) __nonnull((1));
|
||||
|
||||
int pthread_rwlock_destroy(pthread_rwlock_t*) __nonnull((1));
|
||||
int pthread_rwlock_init(pthread_rwlock_t*, const pthread_rwlockattr_t*) __nonnull((1));
|
||||
|
||||
@@ -176,6 +176,27 @@ extern size_t __ctype_get_mb_cur_max(void);
|
||||
#include <android/legacy_stdlib_inlines.h>
|
||||
#endif
|
||||
|
||||
#if defined(__BIONIC_FORTIFY)
|
||||
|
||||
extern char* __realpath_real(const char*, char*) __RENAME(realpath);
|
||||
__errordecl(__realpath_size_error, "realpath output parameter must be NULL or a >= PATH_MAX bytes buffer");
|
||||
|
||||
#if !defined(__clang__)
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
char* realpath(const char* path, char* resolved) {
|
||||
size_t bos = __bos(resolved);
|
||||
|
||||
/* PATH_MAX is unavailable without polluting the namespace, but it's always 4096 on Linux */
|
||||
if (bos != __BIONIC_FORTIFY_UNKNOWN_SIZE && bos < 4096) {
|
||||
__realpath_size_error();
|
||||
}
|
||||
|
||||
return __realpath_real(path, resolved);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined(__BIONIC_FORTIFY) */
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _STDLIB_H */
|
||||
|
||||
@@ -121,6 +121,13 @@ extern char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1));
|
||||
#define __bionic_using_gnu_basename
|
||||
#endif
|
||||
|
||||
extern void* __memchr_chk(const void*, int, size_t, size_t);
|
||||
__errordecl(__memchr_buf_size_error, "memchr called with size bigger than buffer");
|
||||
|
||||
extern void* __memrchr_chk(const void*, int, size_t, size_t);
|
||||
__errordecl(__memrchr_buf_size_error, "memrchr called with size bigger than buffer");
|
||||
extern void* __memrchr_real(const void*, int, size_t) __RENAME(memrchr);
|
||||
|
||||
extern char* __stpncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t);
|
||||
extern char* __strncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t);
|
||||
extern size_t __strlcpy_real(char* __restrict, const char* __restrict, size_t) __RENAME(strlcpy);
|
||||
@@ -130,6 +137,48 @@ extern size_t __strlcat_chk(char* __restrict, const char* __restrict, size_t, si
|
||||
|
||||
#if defined(__BIONIC_FORTIFY)
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
void* memchr(const void *s, int c, size_t n) {
|
||||
size_t bos = __bos(s);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __builtin_memchr(s, c, n);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(n) && (n > bos)) {
|
||||
__memchr_buf_size_error();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(n) && (n <= bos)) {
|
||||
return __builtin_memchr(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __memchr_chk(s, c, n, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
void* memrchr(const void *s, int c, size_t n) {
|
||||
size_t bos = __bos(s);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __memrchr_real(s, c, n);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(n) && (n > bos)) {
|
||||
__memrchr_buf_size_error();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(n) && (n <= bos)) {
|
||||
return __memrchr_real(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __memrchr_chk(s, c, n, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
void* memcpy(void* __restrict dest, const void* __restrict src, size_t copy_amount) {
|
||||
return __builtin___memcpy_chk(dest, src, copy_amount, __bos0(dest));
|
||||
|
||||
@@ -80,7 +80,6 @@ struct prop_msg
|
||||
|
||||
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
|
||||
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
|
||||
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
|
||||
#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
|
||||
#define PROP_PATH_BOOTIMAGE_BUILD "/build.prop"
|
||||
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
|
||||
@@ -99,6 +98,24 @@ int __system_property_set_filename(const char *filename);
|
||||
*/
|
||||
int __system_property_area_init();
|
||||
|
||||
/* Read the global serial number of the system properties
|
||||
**
|
||||
** Called to predict if a series of cached __system_property_find
|
||||
** objects will have seen __system_property_serial values change.
|
||||
** But also aids the converse, as changes in the global serial can
|
||||
** also be used to predict if a failed __system_property_find
|
||||
** could in-turn now find an new object; thus preventing the
|
||||
** cycles of effort to poll __system_property_find.
|
||||
**
|
||||
** Called at the beginning of a cache cycle to signal _any_ possible
|
||||
** changes have occurred since last. If there is, check each individual
|
||||
** __system_property_serial to confirm dirty, or __system_property_find
|
||||
** to check if the property now exists.
|
||||
**
|
||||
** Returns the serial number on success, -1 on error.
|
||||
*/
|
||||
unsigned int __system_property_area_serial();
|
||||
|
||||
/* Add a new system property. Can only be done by a single
|
||||
** process that has write access to the property area, and
|
||||
** that process must handle sequencing to ensure the property
|
||||
|
||||
@@ -224,13 +224,89 @@ extern int tcsetpgrp(int fd, pid_t _pid);
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; })
|
||||
|
||||
extern ssize_t __pread_chk(int, void*, size_t, off_t, size_t);
|
||||
__errordecl(__pread_dest_size_error, "pread called with size bigger than destination");
|
||||
__errordecl(__pread_count_toobig_error, "pread called with count > SSIZE_MAX");
|
||||
extern ssize_t __pread_real(int, void*, size_t, off_t) __RENAME(pread);
|
||||
|
||||
extern ssize_t __pread64_chk(int, void*, size_t, off64_t, size_t);
|
||||
__errordecl(__pread64_dest_size_error, "pread64 called with size bigger than destination");
|
||||
__errordecl(__pread64_count_toobig_error, "pread64 called with count > SSIZE_MAX");
|
||||
extern ssize_t __pread64_real(int, void*, size_t, off64_t) __RENAME(pread64);
|
||||
|
||||
extern ssize_t __read_chk(int, void*, size_t, size_t);
|
||||
__errordecl(__read_dest_size_error, "read called with size bigger than destination");
|
||||
__errordecl(__read_count_toobig_error, "read called with count > SSIZE_MAX");
|
||||
extern ssize_t __read_real(int, void*, size_t) __RENAME(read);
|
||||
|
||||
extern ssize_t __readlink_chk(const char*, char*, size_t, size_t);
|
||||
__errordecl(__readlink_dest_size_error, "readlink called with size bigger than destination");
|
||||
__errordecl(__readlink_size_toobig_error, "readlink called with size > SSIZE_MAX");
|
||||
extern ssize_t __readlink_real(const char*, char*, size_t) __RENAME(readlink);
|
||||
|
||||
extern ssize_t __readlinkat_chk(int dirfd, const char*, char*, size_t, size_t);
|
||||
__errordecl(__readlinkat_dest_size_error, "readlinkat called with size bigger than destination");
|
||||
__errordecl(__readlinkat_size_toobig_error, "readlinkat called with size > SSIZE_MAX");
|
||||
extern ssize_t __readlinkat_real(int dirfd, const char*, char*, size_t) __RENAME(readlinkat);
|
||||
|
||||
#if defined(__BIONIC_FORTIFY)
|
||||
|
||||
#if defined(__USE_FILE_OFFSET64)
|
||||
#define __PREAD_PREFIX(x) __pread64_ ## x
|
||||
#else
|
||||
#define __PREAD_PREFIX(x) __pread_ ## x
|
||||
#endif
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
|
||||
size_t bos = __bos0(buf);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (__builtin_constant_p(count) && (count > SSIZE_MAX)) {
|
||||
__PREAD_PREFIX(count_toobig_error)();
|
||||
}
|
||||
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __PREAD_PREFIX(real)(fd, buf, count, offset);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(count) && (count > bos)) {
|
||||
__PREAD_PREFIX(dest_size_error)();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(count) && (count <= bos)) {
|
||||
return __PREAD_PREFIX(real)(fd, buf, count, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __PREAD_PREFIX(chk)(fd, buf, count, offset, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
ssize_t pread64(int fd, void* buf, size_t count, off64_t offset) {
|
||||
size_t bos = __bos0(buf);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (__builtin_constant_p(count) && (count > SSIZE_MAX)) {
|
||||
__pread64_count_toobig_error();
|
||||
}
|
||||
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __pread64_real(fd, buf, count, offset);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(count) && (count > bos)) {
|
||||
__pread64_dest_size_error();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(count) && (count <= bos)) {
|
||||
return __pread64_real(fd, buf, count, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __pread64_chk(fd, buf, count, offset, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
ssize_t read(int fd, void* buf, size_t count) {
|
||||
size_t bos = __bos0(buf);
|
||||
@@ -255,6 +331,57 @@ ssize_t read(int fd, void* buf, size_t count) {
|
||||
|
||||
return __read_chk(fd, buf, count, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
ssize_t readlink(const char* path, char* buf, size_t size) {
|
||||
size_t bos = __bos(buf);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (__builtin_constant_p(size) && (size > SSIZE_MAX)) {
|
||||
__readlink_size_toobig_error();
|
||||
}
|
||||
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __readlink_real(path, buf, size);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(size) && (size > bos)) {
|
||||
__readlink_dest_size_error();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(size) && (size <= bos)) {
|
||||
return __readlink_real(path, buf, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __readlink_chk(path, buf, size, bos);
|
||||
}
|
||||
|
||||
__BIONIC_FORTIFY_INLINE
|
||||
ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t size) {
|
||||
size_t bos = __bos(buf);
|
||||
|
||||
#if !defined(__clang__)
|
||||
if (__builtin_constant_p(size) && (size > SSIZE_MAX)) {
|
||||
__readlinkat_size_toobig_error();
|
||||
}
|
||||
|
||||
if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
|
||||
return __readlinkat_real(dirfd, path, buf, size);
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(size) && (size > bos)) {
|
||||
__readlinkat_dest_size_error();
|
||||
}
|
||||
|
||||
if (__builtin_constant_p(size) && (size <= bos)) {
|
||||
return __readlinkat_real(dirfd, path, buf, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return __readlinkat_chk(dirfd, path, buf, size, bos);
|
||||
}
|
||||
|
||||
#endif /* defined(__BIONIC_FORTIFY) */
|
||||
|
||||
__END_DECLS
|
||||
|
||||
@@ -14,7 +14,6 @@ if top is None:
|
||||
|
||||
# Set up the env vars for libclang.
|
||||
site.addsitedir(os.path.join(top, 'external/clang/bindings/python'))
|
||||
os.putenv('LD_LIBRARY_PATH', os.path.join(top, 'prebuilts/sdk/tools/linux'))
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import conf
|
||||
@@ -26,6 +25,10 @@ from clang.cindex import TokenGroup
|
||||
from clang.cindex import TokenKind
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
# Set up LD_LIBRARY_PATH to include libclang.so, libLLVM.so, and etc.
|
||||
# Note that setting LD_LIBRARY_PATH with os.putenv() sometimes doesn't help.
|
||||
clang.cindex.Config.set_library_path(os.path.join(top, 'prebuilts/sdk/tools/linux/lib64'))
|
||||
|
||||
from defaults import kCppUndefinedMacro
|
||||
from defaults import kernel_remove_config_macros
|
||||
from defaults import kernel_token_replacements
|
||||
|
||||
74
libc/private/bionic_lock.h
Normal file
74
libc/private/bionic_lock.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BIONIC_LOCK_H
|
||||
#define _BIONIC_LOCK_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include "private/bionic_futex.h"
|
||||
|
||||
class Lock {
|
||||
private:
|
||||
enum LockState {
|
||||
Unlocked = 0,
|
||||
LockedWithoutWaiter,
|
||||
LockedWithWaiter,
|
||||
};
|
||||
_Atomic(LockState) state;
|
||||
bool process_shared;
|
||||
|
||||
public:
|
||||
Lock(bool process_shared = false) {
|
||||
init(process_shared);
|
||||
}
|
||||
|
||||
void init(bool process_shared) {
|
||||
atomic_init(&state, Unlocked);
|
||||
this->process_shared = process_shared;
|
||||
}
|
||||
|
||||
void lock() {
|
||||
LockState old_state = Unlocked;
|
||||
if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
|
||||
LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed))) {
|
||||
return;
|
||||
}
|
||||
while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
|
||||
// TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
|
||||
__futex_wait_ex(&state, process_shared, LockedWithWaiter, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (atomic_exchange_explicit(&state, Unlocked, memory_order_release) == LockedWithWaiter) {
|
||||
__futex_wake_ex(&state, process_shared, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _BIONIC_LOCK_H
|
||||
@@ -97,14 +97,8 @@ enum {
|
||||
#define LIBC_PTHREAD_KEY_RESERVED_COUNT 12
|
||||
|
||||
#if defined(USE_JEMALLOC)
|
||||
/* Following are current pthread keys used internally by jemalloc:
|
||||
* je_thread_allocated_tsd jemalloc
|
||||
* je_arenas_tsd jemalloc
|
||||
* je_tcache_tsd jemalloc
|
||||
* je_tcache_enabled_tsd jemalloc
|
||||
* je_quarantine_tsd jemalloc
|
||||
*/
|
||||
#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 5
|
||||
/* Internally, jemalloc uses a single key for per thread data. */
|
||||
#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 1
|
||||
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT)
|
||||
#else
|
||||
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT
|
||||
|
||||
@@ -35,11 +35,15 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "atexit.h"
|
||||
#include "thread_private.h"
|
||||
#include "private/thread_private.h"
|
||||
|
||||
struct atexit *__atexit;
|
||||
static int restartloop;
|
||||
|
||||
/* BEGIN android-changed: __unregister_atfork is used by __cxa_finalize */
|
||||
extern void __unregister_atfork(void* dso);
|
||||
/* END android-changed */
|
||||
|
||||
/*
|
||||
* Function pointers are stored in a linked list of pages. The list
|
||||
* is initially empty, and pages are allocated on demand. The first
|
||||
@@ -62,7 +66,7 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso)
|
||||
{
|
||||
struct atexit *p = __atexit;
|
||||
struct atexit_fn *fnp;
|
||||
int pgsize = getpagesize();
|
||||
size_t pgsize = getpagesize();
|
||||
int ret = -1;
|
||||
|
||||
if (pgsize < sizeof(*p))
|
||||
@@ -161,6 +165,12 @@ restart:
|
||||
__atexit = NULL;
|
||||
}
|
||||
_ATEXIT_UNLOCK();
|
||||
|
||||
/* BEGIN android-changed: call __unregister_atfork if dso is not null */
|
||||
if (dso != NULL) {
|
||||
__unregister_atfork(dso);
|
||||
}
|
||||
/* END android-changed */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -170,7 +180,7 @@ void
|
||||
__atexit_register_cleanup(void (*func)(void))
|
||||
{
|
||||
struct atexit *p;
|
||||
int pgsize = getpagesize();
|
||||
size_t pgsize = getpagesize();
|
||||
|
||||
if (pgsize < sizeof(*p))
|
||||
return;
|
||||
@@ -32,8 +32,6 @@
|
||||
#include <sys/mman.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "atexit.h"
|
||||
#include "thread_private.h"
|
||||
|
||||
/*
|
||||
* This variable is zero until a process has created a thread.
|
||||
@@ -44,12 +42,21 @@
|
||||
*/
|
||||
int __isthreaded = 0;
|
||||
|
||||
/* BEGIN android-added: using __cxa_finalize and __cxa_thread_finalize */
|
||||
extern void __cxa_finalize(void* dso_handle);
|
||||
extern void __cxa_thread_finalize();
|
||||
/* END android-added */
|
||||
|
||||
/*
|
||||
* Exit, flushing stdio buffers if necessary.
|
||||
*/
|
||||
void
|
||||
exit(int status)
|
||||
{
|
||||
/* BEGIN android-added: call thread_local d-tors */
|
||||
__cxa_thread_finalize();
|
||||
/* END android-added */
|
||||
|
||||
/*
|
||||
* Call functions registered by atexit() or _cxa_atexit()
|
||||
* (including the stdio cleanup routine) and then _exit().
|
||||
@@ -33,9 +33,12 @@ CheckDirExists(bionic_libc_zoneinfo_dir, 'bionic/libc/zoneinfo')
|
||||
CheckDirExists(bionic_libc_tools_zoneinfo_dir, 'bionic/libc/tools/zoneinfo')
|
||||
print 'Found bionic in %s ...' % bionic_dir
|
||||
|
||||
# Find the icu4c directory.
|
||||
icu_dir = os.path.realpath('%s/../external/icu/icu4c/source' % bionic_dir)
|
||||
CheckDirExists(icu_dir, 'external/icu/icu4c/source')
|
||||
# Find the icu directory.
|
||||
icu_dir = os.path.realpath('%s/../external/icu' % bionic_dir)
|
||||
icu4c_dir = os.path.realpath('%s/icu4c/source' % icu_dir)
|
||||
icu4j_dir = os.path.realpath('%s/icu4j' % icu_dir)
|
||||
CheckDirExists(icu4c_dir, 'external/icu/icu4c/source')
|
||||
CheckDirExists(icu4j_dir, 'external/icu/icu4j')
|
||||
print 'Found icu in %s ...' % icu_dir
|
||||
|
||||
|
||||
@@ -116,14 +119,14 @@ def BuildIcuToolsAndData(data_filename):
|
||||
|
||||
# Build the ICU tools.
|
||||
print 'Configuring ICU tools...'
|
||||
subprocess.check_call(['%s/runConfigureICU' % icu_dir, 'Linux'])
|
||||
subprocess.check_call(['%s/runConfigureICU' % icu4c_dir, 'Linux'])
|
||||
|
||||
# Run the ICU tools.
|
||||
os.chdir('tools/tzcode')
|
||||
|
||||
# The tz2icu tool only picks up icuregions and icuzones in they are in the CWD
|
||||
for icu_data_file in [ 'icuregions', 'icuzones']:
|
||||
icu_data_file_source = '%s/tools/tzcode/%s' % (icu_dir, icu_data_file)
|
||||
icu_data_file_source = '%s/tools/tzcode/%s' % (icu4c_dir, icu_data_file)
|
||||
icu_data_file_symlink = './%s' % icu_data_file
|
||||
os.symlink(icu_data_file_source, icu_data_file_symlink)
|
||||
|
||||
@@ -134,7 +137,7 @@ def BuildIcuToolsAndData(data_filename):
|
||||
subprocess.check_call(['make'])
|
||||
|
||||
# Copy the source file to its ultimate destination.
|
||||
icu_txt_data_dir = '%s/data/misc' % icu_dir
|
||||
icu_txt_data_dir = '%s/data/misc' % icu4c_dir
|
||||
print 'Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir
|
||||
shutil.copy('zoneinfo64.txt', icu_txt_data_dir)
|
||||
|
||||
@@ -143,7 +146,7 @@ def BuildIcuToolsAndData(data_filename):
|
||||
subprocess.check_call(['make', 'INCLUDE_UNI_CORE_DATA=1', '-j32'])
|
||||
|
||||
# Copy the .dat file to its ultimate destination.
|
||||
icu_dat_data_dir = '%s/stubdata' % icu_dir
|
||||
icu_dat_data_dir = '%s/stubdata' % icu4c_dir
|
||||
datfiles = glob.glob('data/out/tmp/icudt??l.dat')
|
||||
if len(datfiles) != 1:
|
||||
print 'ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles)
|
||||
@@ -152,6 +155,20 @@ def BuildIcuToolsAndData(data_filename):
|
||||
print 'Copying %s to %s ...' % (datfile, icu_dat_data_dir)
|
||||
shutil.copy(datfile, icu_dat_data_dir)
|
||||
|
||||
# Generate the ICU4J .jar files
|
||||
os.chdir('%s/data' % icu_working_dir)
|
||||
subprocess.check_call(['make', 'icu4j-data'])
|
||||
|
||||
# Copy the ICU4J .jar files to their ultimate destination.
|
||||
icu_jar_data_dir = '%s/main/shared/data' % icu4j_dir
|
||||
jarfiles = glob.glob('out/icu4j/*.jar')
|
||||
if len(jarfiles) != 2:
|
||||
print 'ERROR: Unexpectedly found %d .jar files (%s). Halting.' % (len(jarfiles), jarfiles)
|
||||
sys.exit(1)
|
||||
for jarfile in jarfiles:
|
||||
print 'Copying %s to %s ...' % (jarfile, icu_jar_data_dir)
|
||||
shutil.copy(jarfile, icu_jar_data_dir)
|
||||
|
||||
# Switch back to the original working cwd.
|
||||
os.chdir(original_working_dir)
|
||||
|
||||
|
||||
Binary file not shown.
46
libdl/Android.bp
Normal file
46
libdl/Android.bp
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// libdl
|
||||
//
|
||||
cc_library {
|
||||
|
||||
// NOTE: --exclude-libs=libgcc.a makes sure that any symbols libdl.so pulls from
|
||||
// libgcc.a are made static to libdl.so. This in turn ensures that libraries that
|
||||
// a) pull symbols from libgcc.a and b) depend on libdl.so will not rely on libdl.so
|
||||
// to provide those symbols, but will instead pull them from libgcc.a. Specifically,
|
||||
// we use this property to make sure libc.so has its own copy of the code from
|
||||
// libgcc.a it uses.
|
||||
//
|
||||
// DO NOT REMOVE --exclude-libs!
|
||||
|
||||
ldflags: ["-Wl,--exclude-libs=libgcc.a"],
|
||||
|
||||
// for x86, exclude libgcc_eh.a for the same reasons as above
|
||||
arch: {
|
||||
x86: {
|
||||
ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
|
||||
},
|
||||
x86_64: {
|
||||
ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
|
||||
},
|
||||
},
|
||||
srcs: ["libdl.c"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
],
|
||||
stl: "none",
|
||||
|
||||
name: "libdl",
|
||||
|
||||
// NOTE: libdl needs __aeabi_unwind_cpp_pr0 from libgcc.a but libgcc.a needs a
|
||||
// few symbols from libc. Using --no-undefined here results in having to link
|
||||
// against libc creating a circular dependency which is removed and we end up
|
||||
// with missing symbols. Since this library is just a bunch of stubs, we set
|
||||
// LOCAL_ALLOW_UNDEFINED_SYMBOLS to remove --no-undefined from the linker flags.
|
||||
allow_undefined_symbols: true,
|
||||
system_shared_libs: [],
|
||||
|
||||
asan: false,
|
||||
}
|
||||
381
libm/Android.bp
Normal file
381
libm/Android.bp
Normal file
@@ -0,0 +1,381 @@
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported directive
|
||||
// ifneq ($(TARGET_USE_PRIVATE_LIBM),true)
|
||||
|
||||
bionic_coverage = false
|
||||
|
||||
// TODO: this comes from from upstream's libc, not libm, but it's an
|
||||
// implementation detail that should have hidden visibility, so it needs
|
||||
// to be in whatever library the math code is in.
|
||||
libm_common_src_files = ["digittoint.c"]
|
||||
|
||||
// TODO: this is not in the BSDs.
|
||||
libm_common_src_files += [
|
||||
"significandl.c",
|
||||
"sincos.c",
|
||||
]
|
||||
|
||||
libm_common_src_files += [
|
||||
"upstream-freebsd/lib/msun/bsdsrc/b_exp.c",
|
||||
"upstream-freebsd/lib/msun/bsdsrc/b_log.c",
|
||||
"upstream-freebsd/lib/msun/bsdsrc/b_tgamma.c",
|
||||
"upstream-freebsd/lib/msun/src/catrig.c",
|
||||
"upstream-freebsd/lib/msun/src/catrigf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_acos.c",
|
||||
"upstream-freebsd/lib/msun/src/e_acosf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_acosh.c",
|
||||
"upstream-freebsd/lib/msun/src/e_acoshf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_asin.c",
|
||||
"upstream-freebsd/lib/msun/src/e_asinf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atan2.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atan2f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atanh.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atanhf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_cosh.c",
|
||||
"upstream-freebsd/lib/msun/src/e_coshf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_exp.c",
|
||||
"upstream-freebsd/lib/msun/src/e_expf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_fmod.c",
|
||||
"upstream-freebsd/lib/msun/src/e_fmodf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_gamma.c",
|
||||
"upstream-freebsd/lib/msun/src/e_gammaf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_gammaf_r.c",
|
||||
"upstream-freebsd/lib/msun/src/e_gamma_r.c",
|
||||
"upstream-freebsd/lib/msun/src/e_hypot.c",
|
||||
"upstream-freebsd/lib/msun/src/e_hypotf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_j0.c",
|
||||
"upstream-freebsd/lib/msun/src/e_j0f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_j1.c",
|
||||
"upstream-freebsd/lib/msun/src/e_j1f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_jn.c",
|
||||
"upstream-freebsd/lib/msun/src/e_jnf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_lgamma.c",
|
||||
"upstream-freebsd/lib/msun/src/e_lgammaf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_lgammaf_r.c",
|
||||
"upstream-freebsd/lib/msun/src/e_lgamma_r.c",
|
||||
"upstream-freebsd/lib/msun/src/e_log10.c",
|
||||
"upstream-freebsd/lib/msun/src/e_log10f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_log2.c",
|
||||
"upstream-freebsd/lib/msun/src/e_log2f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_log.c",
|
||||
"upstream-freebsd/lib/msun/src/e_logf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_pow.c",
|
||||
"upstream-freebsd/lib/msun/src/e_powf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_remainder.c",
|
||||
"upstream-freebsd/lib/msun/src/e_remainderf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_rem_pio2.c",
|
||||
"upstream-freebsd/lib/msun/src/e_rem_pio2f.c",
|
||||
"upstream-freebsd/lib/msun/src/e_scalb.c",
|
||||
"upstream-freebsd/lib/msun/src/e_scalbf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sinh.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sinhf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrt.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrtf.c",
|
||||
"upstream-freebsd/lib/msun/src/imprecise.c",
|
||||
"upstream-freebsd/lib/msun/src/k_cos.c",
|
||||
"upstream-freebsd/lib/msun/src/k_cosf.c",
|
||||
"upstream-freebsd/lib/msun/src/k_exp.c",
|
||||
"upstream-freebsd/lib/msun/src/k_expf.c",
|
||||
"upstream-freebsd/lib/msun/src/k_rem_pio2.c",
|
||||
"upstream-freebsd/lib/msun/src/k_sin.c",
|
||||
"upstream-freebsd/lib/msun/src/k_sinf.c",
|
||||
"upstream-freebsd/lib/msun/src/k_tan.c",
|
||||
"upstream-freebsd/lib/msun/src/k_tanf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_asinh.c",
|
||||
"upstream-freebsd/lib/msun/src/s_asinhf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_atan.c",
|
||||
"upstream-freebsd/lib/msun/src/s_atanf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_carg.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cargf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cargl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cbrt.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cbrtf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ccosh.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ccoshf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ceil.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ceilf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cexp.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cexpf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cimag.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cimagf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cimagl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_conj.c",
|
||||
"upstream-freebsd/lib/msun/src/s_conjf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_conjl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_copysign.c",
|
||||
"upstream-freebsd/lib/msun/src/s_copysignf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cos.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cosf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cproj.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cprojf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cprojl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_creal.c",
|
||||
"upstream-freebsd/lib/msun/src/s_crealf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_creall.c",
|
||||
"upstream-freebsd/lib/msun/src/s_csinh.c",
|
||||
"upstream-freebsd/lib/msun/src/s_csinhf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_csqrt.c",
|
||||
"upstream-freebsd/lib/msun/src/s_csqrtf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_csqrtl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ctanh.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ctanhf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_erf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_erff.c",
|
||||
"upstream-freebsd/lib/msun/src/s_exp2.c",
|
||||
"upstream-freebsd/lib/msun/src/s_exp2f.c",
|
||||
"upstream-freebsd/lib/msun/src/s_expm1.c",
|
||||
"upstream-freebsd/lib/msun/src/s_expm1f.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fabs.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fabsf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fdim.c",
|
||||
"upstream-freebsd/lib/msun/src/s_finite.c",
|
||||
"upstream-freebsd/lib/msun/src/s_finitef.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floor.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floorf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fma.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmaf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmax.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmaxf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmin.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fminf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_frexp.c",
|
||||
"upstream-freebsd/lib/msun/src/s_frexpf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ilogb.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ilogbf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llrint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llrintf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llround.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llroundf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_log1p.c",
|
||||
"upstream-freebsd/lib/msun/src/s_log1pf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_logb.c",
|
||||
"upstream-freebsd/lib/msun/src/s_logbf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lrint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lrintf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lround.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lroundf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_modf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_modff.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nan.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nearbyint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nextafter.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nextafterf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_remquo.c",
|
||||
"upstream-freebsd/lib/msun/src/s_remquof.c",
|
||||
"upstream-freebsd/lib/msun/src/s_rint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_rintf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_round.c",
|
||||
"upstream-freebsd/lib/msun/src/s_roundf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_scalbln.c",
|
||||
"upstream-freebsd/lib/msun/src/s_scalbn.c",
|
||||
"upstream-freebsd/lib/msun/src/s_scalbnf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_signgam.c",
|
||||
"upstream-freebsd/lib/msun/src/s_significand.c",
|
||||
"upstream-freebsd/lib/msun/src/s_significandf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_sin.c",
|
||||
"upstream-freebsd/lib/msun/src/s_sinf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tan.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tanf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tanh.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tanhf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tgammaf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_trunc.c",
|
||||
"upstream-freebsd/lib/msun/src/s_truncf.c",
|
||||
"upstream-freebsd/lib/msun/src/w_cabs.c",
|
||||
"upstream-freebsd/lib/msun/src/w_cabsf.c",
|
||||
"upstream-freebsd/lib/msun/src/w_cabsl.c",
|
||||
"upstream-freebsd/lib/msun/src/w_drem.c",
|
||||
"upstream-freebsd/lib/msun/src/w_dremf.c",
|
||||
]
|
||||
|
||||
libm_common_src_files += [
|
||||
"fake_long_double.c",
|
||||
"signbit.c",
|
||||
]
|
||||
|
||||
libm_ld128_src_files = [
|
||||
"upstream-freebsd/lib/msun/src/e_acosl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_acoshl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_asinl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atan2l.c",
|
||||
"upstream-freebsd/lib/msun/src/e_atanhl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_fmodl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_hypotl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_lgammal.c",
|
||||
"upstream-freebsd/lib/msun/src/e_remainderl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrtl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_asinhl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_atanl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cbrtl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ceill.c",
|
||||
"upstream-freebsd/lib/msun/src/s_copysignl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_coshl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_cosl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fabsl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floorl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmal.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmaxl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fminl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_modfl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_frexpl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ilogbl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llrintl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llroundl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_logbl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lrintl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lroundl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nextafterl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nexttoward.c",
|
||||
"upstream-freebsd/lib/msun/src/s_nexttowardf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_remquol.c",
|
||||
"upstream-freebsd/lib/msun/src/s_rintl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_roundl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_scalbnl.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sinhl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_sinl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tanhl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_tanl.c",
|
||||
"upstream-freebsd/lib/msun/src/s_truncl.c",
|
||||
]
|
||||
|
||||
libm_ld128_src_files += [
|
||||
"upstream-freebsd/lib/msun/ld128/invtrig.c",
|
||||
"upstream-freebsd/lib/msun/ld128/e_lgammal_r.c",
|
||||
"upstream-freebsd/lib/msun/ld128/k_cosl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/k_sinl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/k_tanl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/s_erfl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/s_exp2l.c",
|
||||
"upstream-freebsd/lib/msun/ld128/s_expl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/s_logl.c",
|
||||
"upstream-freebsd/lib/msun/ld128/s_nanl.c",
|
||||
]
|
||||
|
||||
// TODO: re-enable i387/e_sqrtf.S for x86, and maybe others.
|
||||
|
||||
libm_common_cflags = [
|
||||
"-DFLT_EVAL_METHOD=0",
|
||||
"-std=c99",
|
||||
"-include freebsd-compat.h",
|
||||
"-Wno-missing-braces",
|
||||
"-Wno-parentheses",
|
||||
"-Wno-sign-compare",
|
||||
"-Wno-uninitialized",
|
||||
"-Wno-unknown-pragmas",
|
||||
"-fvisibility=hidden",
|
||||
]
|
||||
|
||||
// Workaround the GCC "(long)fn -> lfn" optimization bug which will result in
|
||||
// self recursions for lrint, lrintf, and lrintl.
|
||||
// BUG: 14225968
|
||||
libm_common_cflags += [
|
||||
"-fno-builtin-rint",
|
||||
"-fno-builtin-rintf",
|
||||
"-fno-builtin-rintl",
|
||||
]
|
||||
|
||||
libm_common_local_includes = ["upstream-freebsd/lib/msun/src/"]
|
||||
|
||||
libm_ld_local_includes = ["upstream-freebsd/lib/msun/ld128/"]
|
||||
|
||||
//
|
||||
// libm.so and libm.a for target.
|
||||
//
|
||||
cc_library {
|
||||
name: "libm",
|
||||
|
||||
cflags: libm_common_cflags,
|
||||
include_dirs: ["bionic/libc"],
|
||||
local_include_dirs: libm_common_local_includes,
|
||||
srcs: libm_common_src_files,
|
||||
system_shared_libs: ["libc"],
|
||||
|
||||
native_coverage: bionic_coverage,
|
||||
asan: false,
|
||||
|
||||
multilib: {
|
||||
lib64: {
|
||||
srcs: libm_ld128_src_files,
|
||||
local_include_dirs: libm_ld_local_includes,
|
||||
// We'd really like to do this for all architectures, but since this wasn't done
|
||||
// before, these symbols must continue to be exported on LP32 for binary
|
||||
// compatibility.
|
||||
ldflags: ["-Wl,--exclude-libs,libgcc.a"],
|
||||
},
|
||||
},
|
||||
|
||||
// arch-specific settings
|
||||
arch: {
|
||||
arm: {
|
||||
srcs: [
|
||||
"arm/fenv.c",
|
||||
"arm/e_sqrt.S",
|
||||
"arm/e_sqrtf.S",
|
||||
"arm/s_floor.S",
|
||||
],
|
||||
exclude_srcs: [
|
||||
// TODO: these require neon not available in arm
|
||||
"upstream-freebsd/lib/msun/src/e_sqrt.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrtf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floor.c",
|
||||
],
|
||||
instruction_set: "arm",
|
||||
},
|
||||
|
||||
arm64: {
|
||||
srcs: [
|
||||
"arm64/fenv.c",
|
||||
"arm64/ceil.S",
|
||||
"arm64/fma.S",
|
||||
"arm64/floor.S",
|
||||
"arm64/lrint.S",
|
||||
"arm64/rint.S",
|
||||
"arm64/sqrt.S",
|
||||
"arm64/trunc.S",
|
||||
],
|
||||
exclude_srcs: [
|
||||
"upstream-freebsd/lib/msun/src/s_ceil.c",
|
||||
"upstream-freebsd/lib/msun/src/s_ceilf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fma.c",
|
||||
"upstream-freebsd/lib/msun/src/s_fmaf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floor.c",
|
||||
"upstream-freebsd/lib/msun/src/s_floorf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llrint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_llrintf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lrint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_lrintf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_rint.c",
|
||||
"upstream-freebsd/lib/msun/src/s_rintf.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrt.c",
|
||||
"upstream-freebsd/lib/msun/src/e_sqrtf.c",
|
||||
"upstream-freebsd/lib/msun/src/s_trunc.c",
|
||||
"upstream-freebsd/lib/msun/src/s_truncf.c",
|
||||
],
|
||||
},
|
||||
|
||||
mips: {
|
||||
srcs: ["mips/fenv.c"],
|
||||
},
|
||||
|
||||
mips64: {
|
||||
srcs: ["mips/fenv.c"],
|
||||
},
|
||||
|
||||
x86: {
|
||||
local_include_dirs: ["i387"],
|
||||
srcs: ["i387/fenv.c"],
|
||||
// Clang has wrong long double sizes for x86.
|
||||
clang: false,
|
||||
},
|
||||
|
||||
x86_64: {
|
||||
srcs: ["amd64/fenv.c"],
|
||||
// Clang has wrong long double sizes for x86.
|
||||
clang: false,
|
||||
},
|
||||
},
|
||||
|
||||
stl: "none",
|
||||
}
|
||||
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported directive
|
||||
// endif
|
||||
117
linker/Android.bp
Normal file
117
linker/Android.bp
Normal file
@@ -0,0 +1,117 @@
|
||||
linker_cflags = [
|
||||
"-fno-stack-protector",
|
||||
"-Wstrict-overflow=5",
|
||||
"-fvisibility=hidden",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
]
|
||||
|
||||
cc_binary {
|
||||
|
||||
srcs: [
|
||||
"debugger.cpp",
|
||||
"dlfcn.cpp",
|
||||
"linker.cpp",
|
||||
"linker_allocator.cpp",
|
||||
"linker_block_allocator.cpp",
|
||||
"linker_environ.cpp",
|
||||
"linker_libc_support.c",
|
||||
"linker_memory.cpp",
|
||||
"linker_phdr.cpp",
|
||||
"rt.cpp",
|
||||
],
|
||||
|
||||
arch: {
|
||||
arm: {
|
||||
srcs: ["arch/arm/begin.S"],
|
||||
},
|
||||
arm64: {
|
||||
srcs: ["arch/arm64/begin.S"],
|
||||
},
|
||||
x86: {
|
||||
srcs: ["arch/x86/begin.c"],
|
||||
},
|
||||
x86_64: {
|
||||
srcs: ["arch/x86_64/begin.S"],
|
||||
},
|
||||
mips: {
|
||||
srcs: [
|
||||
"arch/mips/begin.S",
|
||||
"linker_mips.cpp",
|
||||
],
|
||||
},
|
||||
mips64: {
|
||||
srcs: [
|
||||
"arch/mips64/begin.S",
|
||||
"linker_mips.cpp",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// -shared is used to overwrite the -Bstatic and -static
|
||||
// flags triggered by LOCAL_FORCE_STATIC_EXECUTABLE.
|
||||
// This dynamic linker is actually a shared object linked with static
|
||||
// libraries.
|
||||
ldflags: [
|
||||
"-shared",
|
||||
"-Wl,-Bsymbolic",
|
||||
"-Wl,--exclude-libs,ALL",
|
||||
],
|
||||
|
||||
cflags: linker_cflags,
|
||||
// TODO: split out the asflags.
|
||||
asflags: linker_cflags,
|
||||
|
||||
target: {
|
||||
android64: {
|
||||
cflags: ["-DTARGET_IS_64_BIT"],
|
||||
},
|
||||
},
|
||||
|
||||
// We need to access Bionic private headers in the linker.
|
||||
local_include_dirs: ["../libc/"],
|
||||
|
||||
conlyflags: ["-std=gnu99"],
|
||||
|
||||
cppflags: [
|
||||
"-std=gnu++11",
|
||||
"-Wold-style-cast",
|
||||
],
|
||||
|
||||
// we don't want crtbegin.o (because we have begin.o), so unset it
|
||||
// just for this module
|
||||
nocrt: true,
|
||||
|
||||
static_libs: [
|
||||
"libc_nomalloc",
|
||||
"libziparchive",
|
||||
"libutils",
|
||||
"libz",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
static_executable: true,
|
||||
|
||||
name: "linker",
|
||||
multilib: {
|
||||
lib32: {
|
||||
stem: "linker",
|
||||
},
|
||||
lib64: {
|
||||
stem: "linker64",
|
||||
},
|
||||
},
|
||||
compile_multilib: "both",
|
||||
|
||||
// Leave the symbols in the shared library so that stack unwinders can
|
||||
// produce meaningful name resolution.
|
||||
strip: "keep_symbols",
|
||||
|
||||
// Insert an extra objcopy step to add prefix to symbols. This is needed to
|
||||
// prevent gdb looking up symbols in the linker by mistake.
|
||||
prefix_symbols: "__dl_",
|
||||
}
|
||||
|
||||
subdirs = ["tests"]
|
||||
@@ -100,7 +100,7 @@ void* dlsym(void* handle, const char* symbol) {
|
||||
}
|
||||
|
||||
soinfo* found = nullptr;
|
||||
ElfW(Sym)* sym = nullptr;
|
||||
const ElfW(Sym)* sym = nullptr;
|
||||
void* caller_addr = __builtin_return_address(0);
|
||||
soinfo* caller = find_containing_library(caller_addr);
|
||||
|
||||
|
||||
@@ -136,6 +136,17 @@ class LinkedList {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T* find_if(F predicate) const {
|
||||
for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
|
||||
if (predicate(e->element)) {
|
||||
return e->element;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t copy_to_array(T* array[], size_t array_length) const {
|
||||
size_t sz = 0;
|
||||
for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008, 2009 The Android Open Source Project
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -55,27 +55,12 @@
|
||||
#include "linker_block_allocator.h"
|
||||
#include "linker_debug.h"
|
||||
#include "linker_environ.h"
|
||||
#include "linker_leb128.h"
|
||||
#include "linker_sleb128.h"
|
||||
#include "linker_phdr.h"
|
||||
#include "linker_relocs.h"
|
||||
#include "linker_reloc_iterators.h"
|
||||
#include "ziparchive/zip_archive.h"
|
||||
|
||||
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
|
||||
*
|
||||
* Do NOT use malloc() and friends or pthread_*() code here.
|
||||
* Don't use printf() either; it's caused mysterious memory
|
||||
* corruption in the past.
|
||||
* The linker runs before we bring up libc and it's easiest
|
||||
* to make sure it does not depend on any complex libc features
|
||||
*
|
||||
* open issues / todo:
|
||||
*
|
||||
* - cleaner error reporting
|
||||
* - after linking, set as much stuff as possible to READONLY
|
||||
* and NOEXEC
|
||||
*/
|
||||
|
||||
// Override macros to use C++ style casts
|
||||
#undef ELF_ST_TYPE
|
||||
#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
|
||||
@@ -100,6 +85,9 @@ static const char* const kDefaultLdPaths[] = {
|
||||
nullptr
|
||||
};
|
||||
|
||||
static const ElfW(Versym) kVersymNotNeeded = 0;
|
||||
static const ElfW(Versym) kVersymGlobal = 1;
|
||||
|
||||
static std::vector<std::string> g_ld_library_paths;
|
||||
static std::vector<std::string> g_ld_preload_names;
|
||||
|
||||
@@ -379,8 +367,129 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void
|
||||
return rv;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
|
||||
return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
|
||||
const ElfW(Versym)* soinfo::get_versym(size_t n) const {
|
||||
if (has_min_version(2) && versym_ != nullptr) {
|
||||
return versym_ + n;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ElfW(Addr) soinfo::get_verneed_ptr() const {
|
||||
if (has_min_version(2)) {
|
||||
return verneed_ptr_;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t soinfo::get_verneed_cnt() const {
|
||||
if (has_min_version(2)) {
|
||||
return verneed_cnt_;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) soinfo::get_verdef_ptr() const {
|
||||
if (has_min_version(2)) {
|
||||
return verdef_ptr_;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t soinfo::get_verdef_cnt() const {
|
||||
if (has_min_version(2)) {
|
||||
return verdef_cnt_;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
static bool for_each_verdef(const soinfo* si, F functor) {
|
||||
if (!si->has_min_version(2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t verdef_ptr = si->get_verdef_ptr();
|
||||
if (verdef_ptr == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
size_t verdef_cnt = si->get_verdef_cnt();
|
||||
for (size_t i = 0; i<verdef_cnt; ++i) {
|
||||
const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
|
||||
size_t verdaux_offset = offset + verdef->vd_aux;
|
||||
offset += verdef->vd_next;
|
||||
|
||||
if (verdef->vd_version != 1) {
|
||||
DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s",
|
||||
i, verdef->vd_version, si->get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
|
||||
// "this is the version of the file itself. It must not be used for
|
||||
// matching a symbol. It can be used to match references."
|
||||
//
|
||||
// http://www.akkadia.org/drepper/symbol-versioning
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verdef->vd_cnt == 0) {
|
||||
DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);
|
||||
|
||||
if (functor(i, verdef, verdaux) == true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const {
|
||||
if (vi == nullptr) {
|
||||
*versym = kVersymNotNeeded;
|
||||
return true;
|
||||
}
|
||||
|
||||
*versym = kVersymGlobal;
|
||||
|
||||
return for_each_verdef(this,
|
||||
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
|
||||
if (verdef->vd_hash == vi->elf_hash &&
|
||||
strcmp(vi->name, get_string(verdaux->vda_name)) == 0) {
|
||||
*versym = verdef->vd_ndx;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
|
||||
const version_info* vi,
|
||||
const ElfW(Sym)** symbol) const {
|
||||
uint32_t symbol_index;
|
||||
bool success =
|
||||
is_gnu_hash() ?
|
||||
gnu_lookup(symbol_name, vi, &symbol_index) :
|
||||
elf_lookup(symbol_name, vi, &symbol_index);
|
||||
|
||||
if (success) {
|
||||
*symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
|
||||
@@ -395,7 +504,23 @@ static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
||||
static const ElfW(Versym) kVersymHiddenBit = 0x8000;
|
||||
|
||||
static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
|
||||
// the symbol is hidden if bit 15 of versym is set.
|
||||
return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
|
||||
}
|
||||
|
||||
static inline bool check_symbol_version(const ElfW(Versym) verneed,
|
||||
const ElfW(Versym)* verdef) {
|
||||
return verneed == kVersymNotNeeded ||
|
||||
verdef == nullptr ||
|
||||
verneed == (*verdef & ~kVersymHiddenBit);
|
||||
}
|
||||
|
||||
bool soinfo::gnu_lookup(SymbolName& symbol_name,
|
||||
const version_info* vi,
|
||||
uint32_t* symbol_index) const {
|
||||
uint32_t hash = symbol_name.gnu_hash();
|
||||
uint32_t h2 = hash >> gnu_shift2_;
|
||||
|
||||
@@ -403,6 +528,8 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
||||
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
|
||||
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
|
||||
|
||||
*symbol_index = 0;
|
||||
|
||||
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
@@ -411,7 +538,7 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
||||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// bloom test says "probably yes"...
|
||||
@@ -421,43 +548,77 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
||||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// lookup versym for the version definition in this library
|
||||
// note the difference between "version is not requested" (vi == nullptr)
|
||||
// and "version not found". In the first case verneed is kVersymNotNeeded
|
||||
// which implies that the default version can be accepted; the second case results in
|
||||
// verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
|
||||
// for this library and consider only *global* ones.
|
||||
ElfW(Versym) verneed = 0;
|
||||
if (!find_verdef_version_index(vi, &verneed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ElfW(Sym)* s = symtab_ + n;
|
||||
const ElfW(Versym)* verdef = get_versym(n);
|
||||
// skip hidden versions when verneed == kVersymNotNeeded (0)
|
||||
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
|
||||
continue;
|
||||
}
|
||||
if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
|
||||
check_symbol_version(verneed, verdef) &&
|
||||
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
||||
is_symbol_global_and_defined(this, s)) {
|
||||
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(s->st_value),
|
||||
static_cast<size_t>(s->st_size));
|
||||
return s;
|
||||
*symbol_index = n;
|
||||
return true;
|
||||
}
|
||||
} while ((gnu_chain_[n++] & 1) == 0);
|
||||
|
||||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
|
||||
bool soinfo::elf_lookup(SymbolName& symbol_name,
|
||||
const version_info* vi,
|
||||
uint32_t* symbol_index) const {
|
||||
uint32_t hash = symbol_name.elf_hash();
|
||||
|
||||
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
|
||||
symbol_name.get_name(), get_soname(),
|
||||
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
||||
|
||||
ElfW(Versym) verneed = 0;
|
||||
if (!find_verdef_version_index(vi, &verneed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
|
||||
ElfW(Sym)* s = symtab_ + n;
|
||||
if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
||||
const ElfW(Versym)* verdef = get_versym(n);
|
||||
|
||||
// skip hidden versions when verneed == 0
|
||||
if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check_symbol_version(verneed, verdef) &&
|
||||
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
||||
is_symbol_global_and_defined(this, s)) {
|
||||
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
||||
symbol_name.get_name(), get_soname(),
|
||||
reinterpret_cast<void*>(s->st_value),
|
||||
static_cast<size_t>(s->st_size));
|
||||
return s;
|
||||
*symbol_index = n;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +626,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
|
||||
symbol_name.get_name(), get_soname(),
|
||||
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
||||
|
||||
return nullptr;
|
||||
*symbol_index = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
|
||||
@@ -523,10 +685,11 @@ uint32_t SymbolName::gnu_hash() {
|
||||
return gnu_hash_;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
|
||||
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
|
||||
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
|
||||
SymbolName symbol_name(name);
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
|
||||
/* "This element's presence in a shared object library alters the dynamic linker's
|
||||
* symbol resolution algorithm for references within the library. Instead of starting
|
||||
@@ -541,7 +704,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
*/
|
||||
if (si_from->has_DT_SYMBOLIC) {
|
||||
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
|
||||
s = si_from->find_symbol_by_name(symbol_name);
|
||||
if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = si_from;
|
||||
}
|
||||
@@ -549,10 +715,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
|
||||
// 1. Look for it in global_group
|
||||
if (s == nullptr) {
|
||||
bool error = false;
|
||||
global_group.visit([&](soinfo* global_si) {
|
||||
DEBUG("%s: looking up %s in %s (from global group)",
|
||||
si_from->get_soname(), name, global_si->get_soname());
|
||||
s = global_si->find_symbol_by_name(symbol_name);
|
||||
if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = global_si;
|
||||
return false;
|
||||
@@ -560,10 +731,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Look for it in the local group
|
||||
if (s == nullptr) {
|
||||
bool error = false;
|
||||
local_group.visit([&](soinfo* local_si) {
|
||||
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
|
||||
// we already did this - skip
|
||||
@@ -572,7 +748,11 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
|
||||
DEBUG("%s: looking up %s in %s (from local group)",
|
||||
si_from->get_soname(), name, local_si->get_soname());
|
||||
s = local_si->find_symbol_by_name(symbol_name);
|
||||
if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = local_si;
|
||||
return false;
|
||||
@@ -580,6 +760,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
@@ -590,7 +774,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
||||
reinterpret_cast<void*>((*si_found_in)->load_bias));
|
||||
}
|
||||
|
||||
return s;
|
||||
*symbol = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
class ProtectedDataGuard {
|
||||
@@ -735,13 +920,16 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s
|
||||
|
||||
// This is used by dlsym(3). It performs symbol lookup only within the
|
||||
// specified soinfo object and its dependencies in breadth first order.
|
||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
||||
ElfW(Sym)* result = nullptr;
|
||||
const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
||||
const ElfW(Sym)* result = nullptr;
|
||||
SymbolName symbol_name(name);
|
||||
|
||||
|
||||
walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
|
||||
result = current_soinfo->find_symbol_by_name(symbol_name);
|
||||
if (!current_soinfo->find_symbol_by_name(symbol_name, nullptr, &result)) {
|
||||
result = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != nullptr) {
|
||||
*found = current_soinfo;
|
||||
return false;
|
||||
@@ -758,7 +946,10 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
||||
beginning of the global solist. Otherwise the search starts at the
|
||||
specified soinfo (for RTLD_NEXT).
|
||||
*/
|
||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle) {
|
||||
const ElfW(Sym)* dlsym_linear_lookup(const char* name,
|
||||
soinfo** found,
|
||||
soinfo* caller,
|
||||
void* handle) {
|
||||
SymbolName symbol_name(name);
|
||||
|
||||
soinfo* start = solist;
|
||||
@@ -771,13 +962,16 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
|
||||
}
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
for (soinfo* si = start; si != nullptr; si = si->next) {
|
||||
if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
s = si->find_symbol_by_name(symbol_name);
|
||||
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*found = si;
|
||||
break;
|
||||
@@ -800,7 +994,10 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
|
||||
break;
|
||||
}
|
||||
|
||||
s = si->find_symbol_by_name(symbol_name);
|
||||
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*found = si;
|
||||
break;
|
||||
@@ -1444,6 +1641,93 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
|
||||
return ifunc_addr;
|
||||
}
|
||||
|
||||
const version_info* VersionTracker::get_version_info(ElfW(Versym) source_symver) const {
|
||||
if (source_symver < 2 ||
|
||||
source_symver >= version_infos.size() ||
|
||||
version_infos[source_symver].name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &version_infos[source_symver];
|
||||
}
|
||||
|
||||
void VersionTracker::add_version_info(size_t source_index,
|
||||
ElfW(Word) elf_hash,
|
||||
const char* ver_name,
|
||||
const soinfo* target_si) {
|
||||
if (source_index >= version_infos.size()) {
|
||||
version_infos.resize(source_index+1);
|
||||
}
|
||||
|
||||
version_infos[source_index].elf_hash = elf_hash;
|
||||
version_infos[source_index].name = ver_name;
|
||||
version_infos[source_index].target_si = target_si;
|
||||
}
|
||||
|
||||
bool VersionTracker::init_verneed(const soinfo* si_from) {
|
||||
uintptr_t verneed_ptr = si_from->get_verneed_ptr();
|
||||
|
||||
if (verneed_ptr == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t verneed_cnt = si_from->get_verneed_cnt();
|
||||
|
||||
for (size_t i = 0, offset = 0; i<verneed_cnt; ++i) {
|
||||
const ElfW(Verneed)* verneed = reinterpret_cast<ElfW(Verneed)*>(verneed_ptr + offset);
|
||||
size_t vernaux_offset = offset + verneed->vn_aux;
|
||||
offset += verneed->vn_next;
|
||||
|
||||
if (verneed->vn_version != 1) {
|
||||
DL_ERR("unsupported verneed[%zd] vn_version: %d (expected 1)", i, verneed->vn_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* target_soname = si_from->get_string(verneed->vn_file);
|
||||
// find it in dependencies
|
||||
soinfo* target_si = si_from->get_children().find_if([&](const soinfo* si) {
|
||||
return strcmp(si->get_soname(), target_soname) == 0;
|
||||
});
|
||||
|
||||
if (target_si == nullptr) {
|
||||
DL_ERR("cannot find \"%s\" from verneed[%zd] in DT_NEEDED list for \"%s\"",
|
||||
target_soname, i, si_from->get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j<verneed->vn_cnt; ++j) {
|
||||
const ElfW(Vernaux)* vernaux = reinterpret_cast<ElfW(Vernaux)*>(verneed_ptr + vernaux_offset);
|
||||
vernaux_offset += vernaux->vna_next;
|
||||
|
||||
const ElfW(Word) elf_hash = vernaux->vna_hash;
|
||||
const char* ver_name = si_from->get_string(vernaux->vna_name);
|
||||
ElfW(Half) source_index = vernaux->vna_other;
|
||||
|
||||
add_version_info(source_index, elf_hash, ver_name, target_si);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VersionTracker::init_verdef(const soinfo* si_from) {
|
||||
return for_each_verdef(si_from,
|
||||
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
|
||||
add_version_info(verdef->vd_ndx, verdef->vd_hash,
|
||||
si_from->get_string(verdaux->vda_name), si_from);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool VersionTracker::init(const soinfo* si_from) {
|
||||
if (!si_from->has_min_version(2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return init_verneed(si_from) && init_verdef(si_from);
|
||||
}
|
||||
|
||||
#if !defined(__mips__)
|
||||
#if defined(USE_RELA)
|
||||
static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
|
||||
@@ -1462,6 +1746,12 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
|
||||
template<typename ElfRelIteratorT>
|
||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group) {
|
||||
VersionTracker version_tracker;
|
||||
|
||||
if (!version_tracker.init(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||
const auto rel = rel_iterator.next();
|
||||
if (rel == nullptr) {
|
||||
@@ -1481,12 +1771,32 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa
|
||||
continue;
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
soinfo* lsi = nullptr;
|
||||
|
||||
if (sym != 0) {
|
||||
sym_name = get_string(symtab_[sym].st_name);
|
||||
s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
|
||||
const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
|
||||
ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
|
||||
|
||||
if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
|
||||
// there is no version info for this one
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const version_info* vi = version_tracker.get_version_info(sym_ver);
|
||||
|
||||
if (vi == nullptr) {
|
||||
DL_ERR("cannot find verneed/verdef for version index=%d "
|
||||
"referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (s == nullptr) {
|
||||
// We only allow an undefined symbol if this is a weak reference...
|
||||
s = &symtab_[sym];
|
||||
@@ -1977,6 +2287,14 @@ soinfo::soinfo_list_t& soinfo::get_children() {
|
||||
return g_empty_list;
|
||||
}
|
||||
|
||||
const soinfo::soinfo_list_t& soinfo::get_children() const {
|
||||
if (has_min_version(0)) {
|
||||
return children_;
|
||||
}
|
||||
|
||||
return g_empty_list;
|
||||
}
|
||||
|
||||
soinfo::soinfo_list_t& soinfo::get_parents() {
|
||||
if (has_min_version(0)) {
|
||||
return parents_;
|
||||
@@ -1985,7 +2303,7 @@ soinfo::soinfo_list_t& soinfo::get_parents() {
|
||||
return g_empty_list;
|
||||
}
|
||||
|
||||
ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) {
|
||||
ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
|
||||
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
|
||||
return call_ifunc_resolver(s->st_value + load_bias);
|
||||
}
|
||||
@@ -2111,7 +2429,7 @@ bool soinfo::prelink_image() {
|
||||
/* We can't log anything until the linker is relocated */
|
||||
bool relocating_linker = (flags_ & FLAG_LINKER) != 0;
|
||||
if (!relocating_linker) {
|
||||
INFO("[ linking %s ]", get_soname());
|
||||
INFO("[ linking %s ]", get_realpath());
|
||||
DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags_);
|
||||
}
|
||||
|
||||
@@ -2452,12 +2770,23 @@ bool soinfo::prelink_image() {
|
||||
case DT_BIND_NOW:
|
||||
break;
|
||||
|
||||
// Ignore: bionic does not support symbol versioning...
|
||||
case DT_VERSYM:
|
||||
versym_ = reinterpret_cast<ElfW(Versym)*>(load_bias + d->d_un.d_ptr);
|
||||
break;
|
||||
|
||||
case DT_VERDEF:
|
||||
verdef_ptr_ = load_bias + d->d_un.d_ptr;
|
||||
break;
|
||||
case DT_VERDEFNUM:
|
||||
verdef_cnt_ = d->d_un.d_val;
|
||||
break;
|
||||
|
||||
case DT_VERNEED:
|
||||
verneed_ptr_ = load_bias + d->d_un.d_ptr;
|
||||
break;
|
||||
|
||||
case DT_VERNEEDNUM:
|
||||
verneed_cnt_ = d->d_un.d_val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -2531,7 +2860,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
|
||||
if (android_relocs_size_ > 3 &&
|
||||
android_relocs_[0] == 'A' &&
|
||||
android_relocs_[1] == 'P' &&
|
||||
(android_relocs_[2] == 'U' || android_relocs_[2] == 'S') &&
|
||||
android_relocs_[2] == 'S' &&
|
||||
android_relocs_[3] == '2') {
|
||||
DEBUG("[ android relocating %s ]", get_soname());
|
||||
|
||||
@@ -2539,17 +2868,10 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
|
||||
const uint8_t* packed_relocs = android_relocs_ + 4;
|
||||
const size_t packed_relocs_size = android_relocs_size_ - 4;
|
||||
|
||||
if (android_relocs_[2] == 'U') {
|
||||
relocated = relocate(
|
||||
packed_reloc_iterator<leb128_decoder>(
|
||||
leb128_decoder(packed_relocs, packed_relocs_size)),
|
||||
global_group, local_group);
|
||||
} else { // android_relocs_[2] == 'S'
|
||||
relocated = relocate(
|
||||
packed_reloc_iterator<sleb128_decoder>(
|
||||
sleb128_decoder(packed_relocs, packed_relocs_size)),
|
||||
global_group, local_group);
|
||||
}
|
||||
relocated = relocate(
|
||||
packed_reloc_iterator<sleb128_decoder>(
|
||||
sleb128_decoder(packed_relocs, packed_relocs_size)),
|
||||
global_group, local_group);
|
||||
|
||||
if (!relocated) {
|
||||
return false;
|
||||
@@ -2813,6 +3135,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
|
||||
for (const auto& ld_preload_name : g_ld_preload_names) {
|
||||
needed_library_name_list.push_back(ld_preload_name.c_str());
|
||||
++needed_libraries_count;
|
||||
++ld_preloads_count;
|
||||
}
|
||||
|
||||
for_each_dt_needed(si, [&](const char* name) {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "linked_list.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define DL_ERR(fmt, x...) \
|
||||
do { \
|
||||
@@ -142,6 +143,32 @@ class SymbolName {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
|
||||
};
|
||||
|
||||
struct version_info {
|
||||
version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
|
||||
|
||||
uint32_t elf_hash;
|
||||
const char* name;
|
||||
const soinfo* target_si;
|
||||
};
|
||||
|
||||
// Class used construct version dependency graph.
|
||||
class VersionTracker {
|
||||
public:
|
||||
VersionTracker() = default;
|
||||
bool init(const soinfo* si_from);
|
||||
|
||||
const version_info* get_version_info(ElfW(Versym) source_symver) const;
|
||||
private:
|
||||
bool init_verneed(const soinfo* si_from);
|
||||
bool init_verdef(const soinfo* si_from);
|
||||
void add_version_info(size_t source_index, ElfW(Word) elf_hash,
|
||||
const char* ver_name, const soinfo* target_si);
|
||||
|
||||
std::vector<version_info> version_infos;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(VersionTracker);
|
||||
};
|
||||
|
||||
struct soinfo {
|
||||
public:
|
||||
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
||||
@@ -260,11 +287,16 @@ struct soinfo {
|
||||
void set_dt_flags_1(uint32_t dt_flags_1);
|
||||
|
||||
soinfo_list_t& get_children();
|
||||
const soinfo_list_t& get_children() const;
|
||||
|
||||
soinfo_list_t& get_parents();
|
||||
|
||||
ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name);
|
||||
bool find_symbol_by_name(SymbolName& symbol_name,
|
||||
const version_info* vi,
|
||||
const ElfW(Sym)** symbol) const;
|
||||
|
||||
ElfW(Sym)* find_symbol_by_address(const void* addr);
|
||||
ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
|
||||
ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
|
||||
|
||||
const char* get_string(ElfW(Word) index) const;
|
||||
bool can_unload() const;
|
||||
@@ -292,11 +324,18 @@ struct soinfo {
|
||||
|
||||
const char* get_soname() const;
|
||||
const char* get_realpath() const;
|
||||
const ElfW(Versym)* get_versym(size_t n) const;
|
||||
ElfW(Addr) get_verneed_ptr() const;
|
||||
size_t get_verneed_cnt() const;
|
||||
ElfW(Addr) get_verdef_ptr() const;
|
||||
size_t get_verdef_cnt() const;
|
||||
|
||||
bool find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const;
|
||||
|
||||
private:
|
||||
ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
|
||||
bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
|
||||
ElfW(Sym)* elf_addr_lookup(const void* addr);
|
||||
ElfW(Sym)* gnu_lookup(SymbolName& symbol_name);
|
||||
bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
|
||||
ElfW(Sym)* gnu_addr_lookup(const void* addr);
|
||||
|
||||
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
|
||||
@@ -341,11 +380,20 @@ struct soinfo {
|
||||
const char* soname_;
|
||||
std::string realpath_;
|
||||
|
||||
const ElfW(Versym)* versym_;
|
||||
|
||||
ElfW(Addr) verdef_ptr_;
|
||||
size_t verdef_cnt_;
|
||||
|
||||
ElfW(Addr) verneed_ptr_;
|
||||
size_t verneed_cnt_;
|
||||
|
||||
friend soinfo* get_libdl_info();
|
||||
};
|
||||
|
||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
|
||||
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
|
||||
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol);
|
||||
|
||||
enum RelocationKind {
|
||||
kRelocAbsolute = 0,
|
||||
@@ -364,10 +412,10 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
|
||||
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo);
|
||||
void do_dlclose(soinfo* si);
|
||||
|
||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
|
||||
const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
|
||||
soinfo* find_containing_library(const void* addr);
|
||||
|
||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
|
||||
const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
|
||||
|
||||
void debuggerd_init();
|
||||
extern "C" abort_msg_t* g_abort_message;
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
|
||||
#include "private/KernelArgumentBlock.h"
|
||||
|
||||
static char** _envp;
|
||||
static bool _AT_SECURE_value = true;
|
||||
|
||||
bool get_AT_SECURE() {
|
||||
@@ -150,8 +149,8 @@ static bool __is_unsafe_environment_variable(const char* name) {
|
||||
}
|
||||
|
||||
static void __sanitize_environment_variables() {
|
||||
char** src = _envp;
|
||||
char** dst = _envp;
|
||||
char** src = environ;
|
||||
char** dst = environ;
|
||||
for (; src[0] != nullptr; ++src) {
|
||||
if (!__is_valid_environment_variable(src[0])) {
|
||||
continue;
|
||||
@@ -168,7 +167,7 @@ static void __sanitize_environment_variables() {
|
||||
|
||||
void linker_env_init(KernelArgumentBlock& args) {
|
||||
// Store environment pointer - can't be null.
|
||||
_envp = args.envp;
|
||||
environ = args.envp;
|
||||
|
||||
__init_AT_SECURE(args);
|
||||
__sanitize_environment_variables();
|
||||
@@ -179,7 +178,7 @@ const char* linker_env_get(const char* name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (char** p = _envp; p[0] != nullptr; ++p) {
|
||||
for (char** p = environ; p[0] != nullptr; ++p) {
|
||||
const char* val = env_match(p[0], name);
|
||||
if (val != nullptr) {
|
||||
if (val[0] == '\0') {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "linker_debug.h"
|
||||
#include "linker_relocs.h"
|
||||
#include "linker_reloc_iterators.h"
|
||||
#include "linker_leb128.h"
|
||||
#include "linker_sleb128.h"
|
||||
|
||||
template bool soinfo::relocate<plain_reloc_iterator>(plain_reloc_iterator&& rel_iterator,
|
||||
const soinfo_list_t& global_group,
|
||||
@@ -41,15 +41,16 @@ template bool soinfo::relocate<packed_reloc_iterator<sleb128_decoder>>(
|
||||
const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group);
|
||||
|
||||
template bool soinfo::relocate<packed_reloc_iterator<leb128_decoder>>(
|
||||
packed_reloc_iterator<leb128_decoder>&& rel_iterator,
|
||||
const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group);
|
||||
|
||||
template <typename ElfRelIteratorT>
|
||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
||||
const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group) {
|
||||
VersionTracker version_tracker;
|
||||
|
||||
if (!version_tracker.init(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||
const auto rel = rel_iterator.next();
|
||||
|
||||
@@ -69,12 +70,33 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
||||
continue;
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
soinfo* lsi = nullptr;
|
||||
|
||||
if (sym != 0) {
|
||||
sym_name = get_string(symtab_[sym].st_name);
|
||||
s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
|
||||
const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
|
||||
ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
|
||||
|
||||
if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
|
||||
// there is no version info for this one
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const version_info* vi = version_tracker.get_version_info(sym_ver);
|
||||
|
||||
if (vi == nullptr) {
|
||||
DL_ERR("cannot find verneed/verdef for version index=%d "
|
||||
"referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
// mips does not support relocation with weak-undefined symbols
|
||||
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname());
|
||||
@@ -147,7 +169,11 @@ bool soinfo::mips_relocate_got(const soinfo_list_t& global_group,
|
||||
// This is an undefined reference... try to locate it.
|
||||
const char* sym_name = get_string(sym->st_name);
|
||||
soinfo* lsi = nullptr;
|
||||
ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
// We only allow an undefined symbol if this is a weak reference.
|
||||
s = &symtab_[g];
|
||||
|
||||
@@ -14,42 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LINKER_LEB128_H
|
||||
#define _LINKER_LEB128_H
|
||||
#ifndef _LINKER_SLEB128_H
|
||||
#define _LINKER_SLEB128_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Helper classes for decoding LEB128, used in packed relocation data.
|
||||
// http://en.wikipedia.org/wiki/LEB128
|
||||
|
||||
class leb128_decoder {
|
||||
public:
|
||||
leb128_decoder(const uint8_t* buffer, size_t count)
|
||||
: current_(buffer), end_(buffer + count) { }
|
||||
|
||||
size_t pop_front() {
|
||||
size_t value = 0;
|
||||
|
||||
size_t shift = 0;
|
||||
uint8_t byte;
|
||||
|
||||
do {
|
||||
if (current_ >= end_) {
|
||||
__libc_fatal("leb128_decoder ran out of bounds");
|
||||
}
|
||||
byte = *current_++;
|
||||
value |= static_cast<size_t>(byte & 127) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 128);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t* current_;
|
||||
const uint8_t* const end_;
|
||||
};
|
||||
|
||||
class sleb128_decoder {
|
||||
public:
|
||||
sleb128_decoder(const uint8_t* buffer, size_t count)
|
||||
@@ -64,7 +36,7 @@ class sleb128_decoder {
|
||||
|
||||
do {
|
||||
if (current_ >= end_) {
|
||||
__libc_fatal("leb128_decoder ran out of bounds");
|
||||
__libc_fatal("sleb128_decoder ran out of bounds");
|
||||
}
|
||||
byte = *current_++;
|
||||
value |= (static_cast<size_t>(byte & 127) << shift);
|
||||
@@ -83,5 +55,4 @@ class sleb128_decoder {
|
||||
const uint8_t* const end_;
|
||||
};
|
||||
|
||||
#endif // __LINKER_LEB128_H
|
||||
|
||||
#endif // __LINKER_SLEB128_H
|
||||
47
linker/tests/Android.bp
Normal file
47
linker/tests/Android.bp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Copyright (C) 2012 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_test {
|
||||
name: "linker-unit-tests",
|
||||
multilib: {
|
||||
lib32: {
|
||||
stem: "linker-unit-tests32",
|
||||
},
|
||||
lib64: {
|
||||
stem: "linker-unit-tests64",
|
||||
},
|
||||
},
|
||||
|
||||
cflags: [
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
"-std=gnu++11",
|
||||
],
|
||||
local_include_dirs: ["/../../libc/"],
|
||||
|
||||
srcs: [
|
||||
"linked_list_test.cpp",
|
||||
"linker_block_allocator_test.cpp",
|
||||
"../linker_block_allocator.cpp",
|
||||
"linker_memory_allocator_test.cpp",
|
||||
"../linker_allocator.cpp",
|
||||
// for __libc_fatal
|
||||
"../../libc/bionic/libc_logging.cpp",
|
||||
],
|
||||
}
|
||||
@@ -295,9 +295,7 @@ bionic-unit-tests_shared_libraries_target := \
|
||||
# which bionic does not support. Reenable this once this question is resolved.
|
||||
bionic-unit-tests_clang_target := false
|
||||
|
||||
ifneq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH))
|
||||
bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global
|
||||
endif
|
||||
|
||||
module := bionic-unit-tests
|
||||
module_tag := optional
|
||||
|
||||
@@ -35,7 +35,7 @@ class ClassWithDtor {
|
||||
std::string message;
|
||||
};
|
||||
|
||||
thread_local ClassWithDtor class_with_dtor;
|
||||
static thread_local ClassWithDtor class_with_dtor;
|
||||
|
||||
static void* thread_nop(void* arg) {
|
||||
class_with_dtor.set_message(*static_cast<std::string*>(arg));
|
||||
@@ -50,6 +50,29 @@ TEST(thread_local, smoke) {
|
||||
ASSERT_EQ("dtor called.", class_with_dtor_output);
|
||||
}
|
||||
|
||||
class ClassWithDtorForMainThread {
|
||||
public:
|
||||
void set_message(const std::string& msg) {
|
||||
message = msg;
|
||||
}
|
||||
|
||||
~ClassWithDtorForMainThread() {
|
||||
fprintf(stderr, "%s", message.c_str());
|
||||
}
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
static void thread_atexit_main() {
|
||||
static thread_local ClassWithDtorForMainThread class_with_dtor_for_main_thread;
|
||||
class_with_dtor_for_main_thread.set_message("d-tor for main thread called.");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST(thread_local, dtor_for_main_thread) {
|
||||
ASSERT_EXIT(thread_atexit_main(), testing::ExitedWithCode(0), "d-tor for main thread called.");
|
||||
}
|
||||
|
||||
extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle);
|
||||
|
||||
static void thread_atexit_fn1(void* arg) {
|
||||
|
||||
@@ -134,6 +134,10 @@ TEST_F(DlExtTest, ExtInfoUseFdWithOffset) {
|
||||
|
||||
TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
|
||||
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;
|
||||
// lib_path is relative when $ANDROID_DATA is relative
|
||||
char lib_realpath_buf[PATH_MAX];
|
||||
ASSERT_TRUE(realpath(lib_path.c_str(), lib_realpath_buf) == lib_realpath_buf);
|
||||
const std::string lib_realpath = std::string(lib_realpath_buf);
|
||||
|
||||
android_dlextinfo extinfo;
|
||||
extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
|
||||
@@ -158,7 +162,7 @@ TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
|
||||
extinfo.library_fd_offset = PAGE_SIZE;
|
||||
handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo);
|
||||
ASSERT_TRUE(handle_ == nullptr);
|
||||
ASSERT_EQ("dlopen failed: \"" + lib_path + "\" has bad ELF magic", dlerror());
|
||||
ASSERT_EQ("dlopen failed: \"" + lib_realpath + "\" has bad ELF magic", dlerror());
|
||||
|
||||
close(extinfo.library_fd);
|
||||
}
|
||||
|
||||
@@ -626,7 +626,6 @@ TEST(dlfcn, dlopen_nodelete_dt_flags_1) {
|
||||
}
|
||||
|
||||
TEST(dlfcn, dlsym_df_1_global) {
|
||||
#if !defined(__arm__) && !defined(__aarch64__)
|
||||
void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
int (*get_answer)();
|
||||
@@ -634,9 +633,6 @@ TEST(dlfcn, dlsym_df_1_global) {
|
||||
ASSERT_TRUE(get_answer != nullptr) << dlerror();
|
||||
ASSERT_EQ(42, get_answer());
|
||||
ASSERT_EQ(0, dlclose(handle));
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on arm/arm64 (to be reenabled once b/18137520 or b/18130452 are fixed).\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(dlfcn, dlopen_failure) {
|
||||
@@ -764,7 +760,11 @@ TEST(dlfcn, dladdr_libc) {
|
||||
void* addr = reinterpret_cast<void*>(puts); // well-known libc function
|
||||
ASSERT_TRUE(dladdr(addr, &info) != 0);
|
||||
|
||||
ASSERT_STREQ(BIONIC_PATH_TO_LIBC, info.dli_fname);
|
||||
// /system/lib is symlink when this test is executed on host.
|
||||
char libc_realpath[PATH_MAX];
|
||||
ASSERT_TRUE(realpath(BIONIC_PATH_TO_LIBC, libc_realpath) == libc_realpath);
|
||||
|
||||
ASSERT_STREQ(libc_realpath, info.dli_fname);
|
||||
// TODO: add check for dfi_fbase
|
||||
ASSERT_STREQ("puts", info.dli_sname);
|
||||
ASSERT_EQ(addr, info.dli_saddr);
|
||||
@@ -921,3 +921,63 @@ TEST(dlfcn, dlopen_dlopen_from_ctor) {
|
||||
GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_v1) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(1, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_v2) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv2.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(2, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_other_v2) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv2_other.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(20, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_other_v3) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv3_other.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(3, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_default_via_dlsym) {
|
||||
void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "versioned_function"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(3, fn()); // the default version is 3
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
// This preempts the implementation from libtest_versioned_lib.so
|
||||
extern "C" int version_zero_function() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This preempts the implementation from libtest_versioned_uselibv*.so
|
||||
extern "C" int version_zero_function2() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -623,6 +623,22 @@ TEST_F(DEATHTEST, FD_ISSET_2_fortified) {
|
||||
ASSERT_FORTIFY(FD_ISSET(0, set));
|
||||
}
|
||||
|
||||
TEST_F(DEATHTEST, pread_fortified) {
|
||||
char buf[1];
|
||||
size_t ct = atoi("2"); // prevent optimizations
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
ASSERT_FORTIFY(pread(fd, buf, ct, 0));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_F(DEATHTEST, pread64_fortified) {
|
||||
char buf[1];
|
||||
size_t ct = atoi("2"); // prevent optimizations
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
ASSERT_FORTIFY(pread64(fd, buf, ct, 0));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_F(DEATHTEST, read_fortified) {
|
||||
char buf[1];
|
||||
size_t ct = atoi("2"); // prevent optimizations
|
||||
@@ -631,6 +647,18 @@ TEST_F(DEATHTEST, read_fortified) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_F(DEATHTEST, readlink_fortified) {
|
||||
char buf[1];
|
||||
size_t ct = atoi("2"); // prevent optimizations
|
||||
ASSERT_FORTIFY(readlink("/dev/null", buf, ct));
|
||||
}
|
||||
|
||||
TEST_F(DEATHTEST, readlinkat_fortified) {
|
||||
char buf[1];
|
||||
size_t ct = atoi("2"); // prevent optimizations
|
||||
ASSERT_FORTIFY(readlinkat(AT_FDCWD, "/dev/null", buf, ct));
|
||||
}
|
||||
|
||||
extern "C" char* __strncat_chk(char*, const char*, size_t, size_t);
|
||||
extern "C" char* __strcat_chk(char*, const char*, size_t);
|
||||
|
||||
|
||||
25
tests/libs/Android.build.pthread_atfork.mk
Normal file
25
tests/libs/Android.build.pthread_atfork.mk
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (C) 2014 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# This library used to test phtread_atfork handler behaviour
|
||||
# during/after dlclose.
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_pthread_atfork_src_files := pthread_atfork.cpp
|
||||
|
||||
module := libtest_pthread_atfork
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
120
tests/libs/Android.build.versioned_lib.mk
Normal file
120
tests/libs/Android.build.versioned_lib.mk
Normal file
@@ -0,0 +1,120 @@
|
||||
#
|
||||
# Copyright (C) 2015 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Libraries used to test versioned symbols
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv1_src_files := versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv1_shared_libraries := \
|
||||
libtest_versioned_libv1
|
||||
|
||||
module := libtest_versioned_uselibv1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv2_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv2_shared_libraries := \
|
||||
libtest_versioned_libv2
|
||||
|
||||
libtest_versioned_uselibv2_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_uselib.map
|
||||
|
||||
module := libtest_versioned_uselibv2
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv2_other_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv2_other_shared_libraries := \
|
||||
libtest_versioned_otherlib_empty libtest_versioned_libv2
|
||||
|
||||
module := libtest_versioned_uselibv2_other
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv3_other_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv3_other_shared_libraries := \
|
||||
libtest_versioned_otherlib_empty libtest_versioned_lib
|
||||
|
||||
module := libtest_versioned_uselibv3_other
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# lib v1 - this one used during static linking but never used at runtime
|
||||
# which forces libtest_versioned_uselibv1 to use function v1 from
|
||||
# libtest_versioned_lib.so
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_libv1_src_files := \
|
||||
versioned_lib_v1.cpp
|
||||
|
||||
libtest_versioned_libv1_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v1.map \
|
||||
-Wl,-soname,libtest_versioned_lib.so
|
||||
|
||||
module := libtest_versioned_libv1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# lib v2 - to make libtest_versioned_uselibv2.so use version 2 of versioned_function()
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_libv2_src_files := \
|
||||
versioned_lib_v2.cpp
|
||||
|
||||
libtest_versioned_libv2_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v2.map \
|
||||
-Wl,-soname,libtest_versioned_lib.so
|
||||
|
||||
module := libtest_versioned_libv2
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# last version - this one is used at the runtime and exports 3 versions
|
||||
# of versioned_symbol().
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_lib_src_files := \
|
||||
versioned_lib_v3.cpp
|
||||
|
||||
libtest_versioned_lib_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v3.map
|
||||
|
||||
module := libtest_versioned_lib
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# This library is empty, the actual implementation will provide an unversioned
|
||||
# symbol for versioned_function().
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_otherlib_empty_src_files := empty.cpp
|
||||
|
||||
libtest_versioned_otherlib_empty_ldflags := -Wl,-soname,libtest_versioned_otherlib.so
|
||||
module := libtest_versioned_otherlib_empty
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_otherlib_src_files := versioned_lib_other.cpp
|
||||
|
||||
libtest_versioned_otherlib_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_other.map
|
||||
|
||||
module := libtest_versioned_otherlib
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
@@ -25,7 +25,9 @@ common_additional_dependencies := \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
|
||||
$(LOCAL_PATH)/Android.build.pthread_atfork.mk \
|
||||
$(LOCAL_PATH)/Android.build.testlib.mk \
|
||||
$(LOCAL_PATH)/Android.build.versioned_lib.mk \
|
||||
$(TEST_PATH)/Android.build.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -197,6 +199,16 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk
|
||||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build libtest_versioned_lib.so with its dependencies.
|
||||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.versioned_lib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build libraries needed by pthread_atfork tests
|
||||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.pthread_atfork.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Library with dependency loop used by dlfcn tests
|
||||
#
|
||||
@@ -342,17 +354,17 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
# Library with DF_1_GLOBAL
|
||||
# -----------------------------------------------------------------------------
|
||||
libdl_test_df_1_global_src_files := dl_df_1_global.cpp
|
||||
libdl_test_df_1_global_ldflags := -fuse-ld=bfd -Wl,-z,global
|
||||
module := libdl_test_df_1_global
|
||||
# TODO: re-enable arm once b/18137520 or b/18130452 are fixed
|
||||
ifeq ($(filter $(TARGET_ARCH),arm arm64),)
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
else
|
||||
# build it for host only
|
||||
build_target := SHARED_LIBRARY
|
||||
build_type := host
|
||||
include $(TEST_PATH)/Android.build.mk
|
||||
libdl_test_df_1_global_ldflags := -Wl,-z,global
|
||||
# TODO (dimitry): x86* toolchain does not support -z global - switch to bfd
|
||||
ifeq ($(filter $(TARGET_ARCH),x86 x86_64),$(TARGET_ARCH))
|
||||
libdl_test_df_1_global_ldflags_target := -fuse-ld=bfd
|
||||
endif
|
||||
# TODO (dimitry): host ld.gold does not yet support -z global
|
||||
# remove this line once it is updated.
|
||||
libdl_test_df_1_global_ldflags_host := -fuse-ld=bfd
|
||||
|
||||
module := libdl_test_df_1_global
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Library using symbol from libdl_test_df_1_global
|
||||
|
||||
21
tests/libs/pthread_atfork.cpp
Normal file
21
tests/libs/pthread_atfork.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
extern "C" int proxy_pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
|
||||
return pthread_atfork(prepare, parent, child);
|
||||
}
|
||||
21
tests/libs/versioned_lib_other.cpp
Normal file
21
tests/libs/versioned_lib_other.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" int versioned_function_v2() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
|
||||
9
tests/libs/versioned_lib_other.map
Normal file
9
tests/libs/versioned_lib_other.map
Normal file
@@ -0,0 +1,9 @@
|
||||
TESTLIB_V0 {
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
30
tests/libs/versioned_lib_v1.cpp
Normal file
30
tests/libs/versioned_lib_v1.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v1,versioned_function@@TESTLIB_V1");
|
||||
12
tests/libs/versioned_lib_v1.map
Normal file
12
tests/libs/versioned_lib_v1.map
Normal file
@@ -0,0 +1,12 @@
|
||||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
||||
35
tests/libs/versioned_lib_v2.cpp
Normal file
35
tests/libs/versioned_lib_v2.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v2(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int versioned_function_v2() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 200;
|
||||
}
|
||||
__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
|
||||
__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
|
||||
16
tests/libs/versioned_lib_v2.map
Normal file
16
tests/libs/versioned_lib_v2.map
Normal file
@@ -0,0 +1,16 @@
|
||||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V1;
|
||||
42
tests/libs/versioned_lib_v3.cpp
Normal file
42
tests/libs/versioned_lib_v3.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v2(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v3(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int versioned_function_v2() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int versioned_function_v3() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
|
||||
__asm__(".symver versioned_function_v2,versioned_function@TESTLIB_V2");
|
||||
__asm__(".symver versioned_function_v3,versioned_function@@TESTLIB_V3");
|
||||
21
tests/libs/versioned_lib_v3.map
Normal file
21
tests/libs/versioned_lib_v3.map
Normal file
@@ -0,0 +1,21 @@
|
||||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V1;
|
||||
|
||||
TESTLIB_V3 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V2;
|
||||
32
tests/libs/versioned_uselib.cpp
Normal file
32
tests/libs/versioned_uselib.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function();
|
||||
|
||||
int get_function_version();
|
||||
int version_zero_function();
|
||||
int version_zero_function2() __attribute__((weak));
|
||||
}
|
||||
|
||||
int get_function_version() {
|
||||
return version_zero_function2() + version_zero_function() + versioned_function();
|
||||
}
|
||||
|
||||
// we expect this function to be preempted by main executable.
|
||||
int version_zero_function2() {
|
||||
return 40000;
|
||||
}
|
||||
9
tests/libs/versioned_uselib.map
Normal file
9
tests/libs/versioned_uselib.map
Normal file
@@ -0,0 +1,9 @@
|
||||
TESTLIB_NONE {
|
||||
global:
|
||||
get_function_version;
|
||||
};
|
||||
|
||||
TESTLIB_ZERO {
|
||||
global:
|
||||
version_zero_function2;
|
||||
} TESTLIB_NONE;
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
@@ -181,6 +182,19 @@ TEST(pthread, pthread_key_dirty) {
|
||||
ASSERT_EQ(0, pthread_key_delete(key));
|
||||
}
|
||||
|
||||
TEST(pthread, static_pthread_key_used_before_creation) {
|
||||
#if defined(__BIONIC__)
|
||||
// See http://b/19625804. The bug is about a static/global pthread key being used before creation.
|
||||
// So here tests if the static/global default value 0 can be detected as invalid key.
|
||||
static pthread_key_t key;
|
||||
ASSERT_EQ(nullptr, pthread_getspecific(key));
|
||||
ASSERT_EQ(EINVAL, pthread_setspecific(key, nullptr));
|
||||
ASSERT_EQ(EINVAL, pthread_key_delete(key));
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test tests bionic pthread key implementation detail.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
static void* IdFn(void* arg) {
|
||||
return arg;
|
||||
}
|
||||
@@ -390,7 +404,9 @@ TEST(pthread, pthread_sigmask) {
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_setname_np__too_long) {
|
||||
ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "this name is far too long for linux"));
|
||||
// The limit is 15 characters --- the kernel's buffer is 16, but includes a NUL.
|
||||
ASSERT_EQ(0, pthread_setname_np(pthread_self(), "123456789012345"));
|
||||
ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "1234567890123456"));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_setname_np__self) {
|
||||
@@ -660,6 +676,37 @@ TEST(pthread, pthread_attr_setstacksize) {
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_rwlockattr_smoke) {
|
||||
pthread_rwlockattr_t attr;
|
||||
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
|
||||
|
||||
int pshared_value_array[] = {PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED};
|
||||
for (size_t i = 0; i < sizeof(pshared_value_array) / sizeof(pshared_value_array[0]); ++i) {
|
||||
ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, pshared_value_array[i]));
|
||||
int pshared;
|
||||
ASSERT_EQ(0, pthread_rwlockattr_getpshared(&attr, &pshared));
|
||||
ASSERT_EQ(pshared_value_array[i], pshared);
|
||||
}
|
||||
|
||||
int kind_array[] = {PTHREAD_RWLOCK_PREFER_READER_NP,
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP};
|
||||
for (size_t i = 0; i < sizeof(kind_array) / sizeof(kind_array[0]); ++i) {
|
||||
ASSERT_EQ(0, pthread_rwlockattr_setkind_np(&attr, kind_array[i]));
|
||||
int kind;
|
||||
ASSERT_EQ(0, pthread_rwlockattr_getkind_np(&attr, &kind));
|
||||
ASSERT_EQ(kind_array[i], kind);
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_rwlock_init_same_as_PTHREAD_RWLOCK_INITIALIZER) {
|
||||
pthread_rwlock_t lock1 = PTHREAD_RWLOCK_INITIALIZER;
|
||||
pthread_rwlock_t lock2;
|
||||
ASSERT_EQ(0, pthread_rwlock_init(&lock2, NULL));
|
||||
ASSERT_EQ(0, memcmp(&lock1, &lock2, sizeof(lock1)));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_rwlock_smoke) {
|
||||
pthread_rwlock_t l;
|
||||
ASSERT_EQ(0, pthread_rwlock_init(&l, NULL));
|
||||
@@ -695,7 +742,6 @@ TEST(pthread, pthread_rwlock_smoke) {
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&l));
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
||||
|
||||
#ifdef __BIONIC__
|
||||
// EDEADLK in "read after write"
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&l));
|
||||
ASSERT_EQ(EDEADLK, pthread_rwlock_rdlock(&l));
|
||||
@@ -705,7 +751,6 @@ TEST(pthread, pthread_rwlock_smoke) {
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&l));
|
||||
ASSERT_EQ(EDEADLK, pthread_rwlock_wrlock(&l));
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(0, pthread_rwlock_destroy(&l));
|
||||
}
|
||||
@@ -807,6 +852,111 @@ TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
|
||||
ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
|
||||
}
|
||||
|
||||
class RwlockKindTestHelper {
|
||||
private:
|
||||
struct ThreadArg {
|
||||
RwlockKindTestHelper* helper;
|
||||
std::atomic<pid_t>& tid;
|
||||
|
||||
ThreadArg(RwlockKindTestHelper* helper, std::atomic<pid_t>& tid)
|
||||
: helper(helper), tid(tid) { }
|
||||
};
|
||||
|
||||
public:
|
||||
pthread_rwlock_t lock;
|
||||
|
||||
public:
|
||||
RwlockKindTestHelper(int kind_type) {
|
||||
InitRwlock(kind_type);
|
||||
}
|
||||
|
||||
~RwlockKindTestHelper() {
|
||||
DestroyRwlock();
|
||||
}
|
||||
|
||||
void CreateWriterThread(pthread_t& thread, std::atomic<pid_t>& tid) {
|
||||
tid = 0;
|
||||
ThreadArg* arg = new ThreadArg(this, tid);
|
||||
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
||||
reinterpret_cast<void* (*)(void*)>(WriterThreadFn), arg));
|
||||
}
|
||||
|
||||
void CreateReaderThread(pthread_t& thread, std::atomic<pid_t>& tid) {
|
||||
tid = 0;
|
||||
ThreadArg* arg = new ThreadArg(this, tid);
|
||||
ASSERT_EQ(0, pthread_create(&thread, NULL,
|
||||
reinterpret_cast<void* (*)(void*)>(ReaderThreadFn), arg));
|
||||
}
|
||||
|
||||
private:
|
||||
void InitRwlock(int kind_type) {
|
||||
pthread_rwlockattr_t attr;
|
||||
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
|
||||
ASSERT_EQ(0, pthread_rwlockattr_setkind_np(&attr, kind_type));
|
||||
ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr));
|
||||
ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr));
|
||||
}
|
||||
|
||||
void DestroyRwlock() {
|
||||
ASSERT_EQ(0, pthread_rwlock_destroy(&lock));
|
||||
}
|
||||
|
||||
static void WriterThreadFn(ThreadArg* arg) {
|
||||
arg->tid = gettid();
|
||||
|
||||
RwlockKindTestHelper* helper = arg->helper;
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&helper->lock));
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&helper->lock));
|
||||
delete arg;
|
||||
}
|
||||
|
||||
static void ReaderThreadFn(ThreadArg* arg) {
|
||||
arg->tid = gettid();
|
||||
|
||||
RwlockKindTestHelper* helper = arg->helper;
|
||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&helper->lock));
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&helper->lock));
|
||||
delete arg;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(pthread, pthread_rwlock_kind_PTHREAD_RWLOCK_PREFER_READER_NP) {
|
||||
RwlockKindTestHelper helper(PTHREAD_RWLOCK_PREFER_READER_NP);
|
||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&helper.lock));
|
||||
|
||||
pthread_t writer_thread;
|
||||
std::atomic<pid_t> writer_tid;
|
||||
helper.CreateWriterThread(writer_thread, writer_tid);
|
||||
WaitUntilThreadSleep(writer_tid);
|
||||
|
||||
pthread_t reader_thread;
|
||||
std::atomic<pid_t> reader_tid;
|
||||
helper.CreateReaderThread(reader_thread, reader_tid);
|
||||
ASSERT_EQ(0, pthread_join(reader_thread, NULL));
|
||||
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&helper.lock));
|
||||
ASSERT_EQ(0, pthread_join(writer_thread, NULL));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_rwlock_kind_PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) {
|
||||
RwlockKindTestHelper helper(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&helper.lock));
|
||||
|
||||
pthread_t writer_thread;
|
||||
std::atomic<pid_t> writer_tid;
|
||||
helper.CreateWriterThread(writer_thread, writer_tid);
|
||||
WaitUntilThreadSleep(writer_tid);
|
||||
|
||||
pthread_t reader_thread;
|
||||
std::atomic<pid_t> reader_tid;
|
||||
helper.CreateReaderThread(reader_thread, reader_tid);
|
||||
WaitUntilThreadSleep(reader_tid);
|
||||
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&helper.lock));
|
||||
ASSERT_EQ(0, pthread_join(writer_thread, NULL));
|
||||
ASSERT_EQ(0, pthread_join(reader_thread, NULL));
|
||||
}
|
||||
|
||||
static int g_once_fn_call_count = 0;
|
||||
static void OnceFn() {
|
||||
++g_once_fn_call_count;
|
||||
@@ -840,14 +990,14 @@ TEST(pthread, pthread_once_1934122) {
|
||||
}
|
||||
|
||||
static int g_atfork_prepare_calls = 0;
|
||||
static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 1; }
|
||||
static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 2; }
|
||||
static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 1; }
|
||||
static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 2; }
|
||||
static int g_atfork_parent_calls = 0;
|
||||
static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 1; }
|
||||
static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 2; }
|
||||
static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 1; }
|
||||
static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 2; }
|
||||
static int g_atfork_child_calls = 0;
|
||||
static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 1; }
|
||||
static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 2; }
|
||||
static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 1; }
|
||||
static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 2; }
|
||||
|
||||
TEST(pthread, pthread_atfork_smoke) {
|
||||
ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1));
|
||||
@@ -858,13 +1008,71 @@ TEST(pthread, pthread_atfork_smoke) {
|
||||
|
||||
// Child and parent calls are made in the order they were registered.
|
||||
if (pid == 0) {
|
||||
ASSERT_EQ(0x12, g_atfork_child_calls);
|
||||
ASSERT_EQ(12, g_atfork_child_calls);
|
||||
_exit(0);
|
||||
}
|
||||
ASSERT_EQ(0x12, g_atfork_parent_calls);
|
||||
ASSERT_EQ(12, g_atfork_parent_calls);
|
||||
|
||||
// Prepare calls are made in the reverse order.
|
||||
ASSERT_EQ(0x21, g_atfork_prepare_calls);
|
||||
ASSERT_EQ(21, g_atfork_prepare_calls);
|
||||
int status;
|
||||
ASSERT_EQ(pid, waitpid(pid, &status, 0));
|
||||
}
|
||||
|
||||
static void AtForkPrepare3() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 3; }
|
||||
static void AtForkPrepare4() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 4; }
|
||||
|
||||
static void AtForkParent3() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 3; }
|
||||
static void AtForkParent4() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 4; }
|
||||
|
||||
static void AtForkChild3() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 3; }
|
||||
static void AtForkChild4() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 4; }
|
||||
|
||||
TEST(pthread, pthread_atfork_with_dlclose) {
|
||||
ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1));
|
||||
|
||||
void* handle = dlopen("libtest_pthread_atfork.so", RTLD_NOW | RTLD_LOCAL);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)(void (*)(void), void (*)(void), void (*)(void));
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "proxy_pthread_atfork"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
// the library registers 2 additional atfork handlers in a constructor
|
||||
ASSERT_EQ(0, fn(AtForkPrepare2, AtForkParent2, AtForkChild2));
|
||||
ASSERT_EQ(0, fn(AtForkPrepare3, AtForkParent3, AtForkChild3));
|
||||
|
||||
ASSERT_EQ(0, pthread_atfork(AtForkPrepare4, AtForkParent4, AtForkChild4));
|
||||
|
||||
int pid = fork();
|
||||
|
||||
ASSERT_NE(-1, pid) << strerror(errno);
|
||||
|
||||
if (pid == 0) {
|
||||
ASSERT_EQ(1234, g_atfork_child_calls);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(1234, g_atfork_parent_calls);
|
||||
ASSERT_EQ(4321, g_atfork_prepare_calls);
|
||||
|
||||
EXPECT_EQ(0, dlclose(handle));
|
||||
g_atfork_prepare_calls = g_atfork_parent_calls = g_atfork_child_calls = 0;
|
||||
|
||||
int status;
|
||||
ASSERT_EQ(pid, waitpid(pid, &status, 0));
|
||||
|
||||
pid = fork();
|
||||
|
||||
ASSERT_NE(-1, pid) << strerror(errno);
|
||||
|
||||
if (pid == 0) {
|
||||
ASSERT_EQ(14, g_atfork_child_calls);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(14, g_atfork_parent_calls);
|
||||
ASSERT_EQ(41, g_atfork_prepare_calls);
|
||||
|
||||
ASSERT_EQ(pid, waitpid(pid, &status, 0));
|
||||
}
|
||||
|
||||
TEST(pthread, pthread_attr_getscope) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
|
||||
#include "ScopedSignalHandler.h"
|
||||
|
||||
@@ -197,7 +198,7 @@ TEST(time, timer_create) {
|
||||
ASSERT_EQ(0, timer_delete(timer_id));
|
||||
}
|
||||
|
||||
static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0;
|
||||
static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
|
||||
static void timer_create_SIGEV_SIGNAL_signal_handler(int signal_number) {
|
||||
++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
|
||||
ASSERT_EQ(SIGUSR1, signal_number);
|
||||
@@ -212,6 +213,7 @@ TEST(time, timer_create_SIGEV_SIGNAL) {
|
||||
timer_t timer_id;
|
||||
ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id));
|
||||
|
||||
timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0;
|
||||
ScopedSignalHandler ssh(SIGUSR1, timer_create_SIGEV_SIGNAL_signal_handler);
|
||||
|
||||
ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
|
||||
@@ -228,25 +230,26 @@ TEST(time, timer_create_SIGEV_SIGNAL) {
|
||||
}
|
||||
|
||||
struct Counter {
|
||||
volatile int value;
|
||||
private:
|
||||
std::atomic<int> value;
|
||||
timer_t timer_id;
|
||||
sigevent_t se;
|
||||
bool timer_valid;
|
||||
|
||||
Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) {
|
||||
memset(&se, 0, sizeof(se));
|
||||
se.sigev_notify = SIGEV_THREAD;
|
||||
se.sigev_notify_function = fn;
|
||||
se.sigev_value.sival_ptr = this;
|
||||
Create();
|
||||
}
|
||||
|
||||
void Create() {
|
||||
ASSERT_FALSE(timer_valid);
|
||||
ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id));
|
||||
timer_valid = true;
|
||||
}
|
||||
|
||||
public:
|
||||
Counter(void (*fn)(sigval_t)) : value(0), timer_valid(false) {
|
||||
memset(&se, 0, sizeof(se));
|
||||
se.sigev_notify = SIGEV_THREAD;
|
||||
se.sigev_notify_function = fn;
|
||||
se.sigev_value.sival_ptr = this;
|
||||
Create();
|
||||
}
|
||||
void DeleteTimer() {
|
||||
ASSERT_TRUE(timer_valid);
|
||||
ASSERT_EQ(0, timer_delete(timer_id));
|
||||
@@ -259,12 +262,16 @@ struct Counter {
|
||||
}
|
||||
}
|
||||
|
||||
int Value() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
void SetTime(time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) {
|
||||
::SetTime(timer_id, value_s, value_ns, interval_s, interval_ns);
|
||||
}
|
||||
|
||||
bool ValueUpdated() {
|
||||
volatile int current_value = value;
|
||||
int current_value = value;
|
||||
time_t start = time(NULL);
|
||||
while (current_value == value && (time(NULL) - start) < 5) {
|
||||
}
|
||||
@@ -287,30 +294,29 @@ struct Counter {
|
||||
|
||||
TEST(time, timer_settime_0) {
|
||||
Counter counter(Counter::CountAndDisarmNotifyFunction);
|
||||
ASSERT_TRUE(counter.timer_valid);
|
||||
|
||||
ASSERT_EQ(0, counter.value);
|
||||
ASSERT_EQ(0, counter.Value());
|
||||
|
||||
counter.SetTime(0, 1, 1, 0);
|
||||
usleep(500000);
|
||||
|
||||
// The count should just be 1 because we disarmed the timer the first time it fired.
|
||||
ASSERT_EQ(1, counter.value);
|
||||
ASSERT_EQ(1, counter.Value());
|
||||
}
|
||||
|
||||
TEST(time, timer_settime_repeats) {
|
||||
Counter counter(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter.timer_valid);
|
||||
|
||||
ASSERT_EQ(0, counter.value);
|
||||
ASSERT_EQ(0, counter.Value());
|
||||
|
||||
counter.SetTime(0, 1, 0, 10);
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
counter.DeleteTimer();
|
||||
// Add a sleep as other threads may be calling the callback function when the timer is deleted.
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
static int timer_create_NULL_signal_handler_invocation_count = 0;
|
||||
static int timer_create_NULL_signal_handler_invocation_count;
|
||||
static void timer_create_NULL_signal_handler(int signal_number) {
|
||||
++timer_create_NULL_signal_handler_invocation_count;
|
||||
ASSERT_EQ(SIGALRM, signal_number);
|
||||
@@ -321,6 +327,7 @@ TEST(time, timer_create_NULL) {
|
||||
timer_t timer_id;
|
||||
ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &timer_id));
|
||||
|
||||
timer_create_NULL_signal_handler_invocation_count = 0;
|
||||
ScopedSignalHandler ssh(SIGALRM, timer_create_NULL_signal_handler);
|
||||
|
||||
ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count);
|
||||
@@ -367,22 +374,59 @@ TEST(time, timer_delete_multiple) {
|
||||
|
||||
TEST(time, timer_create_multiple) {
|
||||
Counter counter1(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter1.timer_valid);
|
||||
Counter counter2(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter2.timer_valid);
|
||||
Counter counter3(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter3.timer_valid);
|
||||
|
||||
ASSERT_EQ(0, counter1.value);
|
||||
ASSERT_EQ(0, counter2.value);
|
||||
ASSERT_EQ(0, counter3.value);
|
||||
ASSERT_EQ(0, counter1.Value());
|
||||
ASSERT_EQ(0, counter2.Value());
|
||||
ASSERT_EQ(0, counter3.Value());
|
||||
|
||||
counter2.SetTime(0, 1, 0, 0);
|
||||
usleep(500000);
|
||||
|
||||
EXPECT_EQ(0, counter1.value);
|
||||
EXPECT_EQ(1, counter2.value);
|
||||
EXPECT_EQ(0, counter3.value);
|
||||
EXPECT_EQ(0, counter1.Value());
|
||||
EXPECT_EQ(1, counter2.Value());
|
||||
EXPECT_EQ(0, counter3.Value());
|
||||
}
|
||||
|
||||
// Test to verify that disarming a repeatable timer disables the callbacks.
|
||||
TEST(time, timer_disarm_terminates) {
|
||||
Counter counter(Counter::CountNotifyFunction);
|
||||
ASSERT_EQ(0, counter.Value());
|
||||
|
||||
counter.SetTime(0, 1, 0, 1);
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
|
||||
counter.SetTime(0, 0, 0, 0);
|
||||
// Add a sleep as the kernel may have pending events when the timer is disarmed.
|
||||
usleep(500000);
|
||||
int value = counter.Value();
|
||||
usleep(500000);
|
||||
|
||||
// Verify the counter has not been incremented.
|
||||
ASSERT_EQ(value, counter.Value());
|
||||
}
|
||||
|
||||
// Test to verify that deleting a repeatable timer disables the callbacks.
|
||||
TEST(time, timer_delete_terminates) {
|
||||
Counter counter(Counter::CountNotifyFunction);
|
||||
ASSERT_EQ(0, counter.Value());
|
||||
|
||||
counter.SetTime(0, 1, 0, 1);
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
|
||||
counter.DeleteTimer();
|
||||
// Add a sleep as other threads may be calling the callback function when the timer is deleted.
|
||||
usleep(500000);
|
||||
int value = counter.Value();
|
||||
usleep(500000);
|
||||
|
||||
// Verify the counter has not been incremented.
|
||||
ASSERT_EQ(value, counter.Value());
|
||||
}
|
||||
|
||||
struct TimerDeleteData {
|
||||
@@ -499,45 +543,3 @@ TEST(time, clock_nanosleep) {
|
||||
timespec out;
|
||||
ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &out));
|
||||
}
|
||||
|
||||
// Test to verify that disarming a repeatable timer disables the
|
||||
// callbacks.
|
||||
TEST(time, timer_disarm_terminates) {
|
||||
Counter counter(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter.timer_valid);
|
||||
|
||||
ASSERT_EQ(0, counter.value);
|
||||
|
||||
counter.SetTime(0, 1, 0, 1);
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
|
||||
counter.SetTime(0, 0, 1, 0);
|
||||
volatile int value = counter.value;
|
||||
usleep(500000);
|
||||
|
||||
// Verify the counter has not been incremented.
|
||||
ASSERT_EQ(value, counter.value);
|
||||
}
|
||||
|
||||
// Test to verify that deleting a repeatable timer disables the
|
||||
// callbacks.
|
||||
TEST(time, timer_delete_terminates) {
|
||||
Counter counter(Counter::CountNotifyFunction);
|
||||
ASSERT_TRUE(counter.timer_valid);
|
||||
|
||||
ASSERT_EQ(0, counter.value);
|
||||
|
||||
counter.SetTime(0, 1, 0, 1);
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
ASSERT_TRUE(counter.ValueUpdated());
|
||||
|
||||
counter.DeleteTimer();
|
||||
volatile int value = counter.value;
|
||||
usleep(500000);
|
||||
|
||||
// Verify the counter has not been incremented.
|
||||
ASSERT_EQ(value, counter.value);
|
||||
}
|
||||
|
||||
1
tools/Android.bp
Normal file
1
tools/Android.bp
Normal file
@@ -0,0 +1 @@
|
||||
subdirs = ["relocation_packer"]
|
||||
@@ -8,11 +8,11 @@ Dependencies
|
||||
------------
|
||||
|
||||
* Python 2.7
|
||||
* [Advanced Python Scheduler](https://apscheduler.readthedocs.org/en/latest/)
|
||||
* [Flask](http://flask.pocoo.org/)
|
||||
* [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation)
|
||||
* [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi)
|
||||
* [Requests](http://docs.python-requests.org/en/latest/)
|
||||
* [termcolor](https://pypi.python.org/pypi/termcolor)
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from flask import Flask, request
|
||||
import requests
|
||||
import termcolor
|
||||
|
||||
import gerrit
|
||||
import tasks
|
||||
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@@ -43,7 +47,7 @@ def handle_build_message():
|
||||
ref = params['REF']
|
||||
patch_set = ref.split('/')[-1]
|
||||
|
||||
print '{} #{} {}: {}'.format(name, number, status, full_url)
|
||||
logging.debug('%s #%s %s: %s', name, number, status, full_url)
|
||||
|
||||
# bionic-lint is always broken, so we don't want to reject changes for
|
||||
# those failures until we clean things up.
|
||||
@@ -69,19 +73,19 @@ def handle_build_message():
|
||||
patch_set))
|
||||
|
||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||
print 'POST {}: {}'.format(url, request_data)
|
||||
print requests.post(url, headers=headers, json=request_data)
|
||||
logging.debug('POST %s: %s', url, request_data)
|
||||
requests.post(url, headers=headers, json=request_data)
|
||||
elif name == 'clean-bionic-presubmit':
|
||||
request_data = {'message': 'out/ directory removed'}
|
||||
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
||||
patch_set))
|
||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||
print 'POST {}: {}'.format(url, request_data)
|
||||
print requests.post(url, headers=headers, json=request_data)
|
||||
logging.debug('POST %s: %s', url, request_data)
|
||||
requests.post(url, headers=headers, json=request_data)
|
||||
elif name == 'bionic-lint':
|
||||
print 'IGNORED'
|
||||
logging.warning('Result for bionic-lint ignored')
|
||||
else:
|
||||
print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name)
|
||||
logging.error('Unknown project: %s', name)
|
||||
return ''
|
||||
|
||||
|
||||
@@ -100,19 +104,31 @@ def drop_rejection():
|
||||
bb_review = 0
|
||||
|
||||
if bb_review >= 0:
|
||||
print 'No rejection to drop: {} {}'.format(change_id, patch_set)
|
||||
logging.info('No rejection to drop: %s %s', change_id, patch_set)
|
||||
return ''
|
||||
|
||||
print 'Dropping rejection: {} {}'.format(change_id, patch_set)
|
||||
logging.info('Dropping rejection: %s %s', change_id, patch_set)
|
||||
|
||||
request_data = {'labels': {'Verified': 0}}
|
||||
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
||||
patch_set))
|
||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||
print 'POST {}: {}'.format(url, request_data)
|
||||
print requests.post(url, headers=headers, json=request_data)
|
||||
logging.debug('POST %s: %s', url, request_data)
|
||||
requests.post(url, headers=headers, json=request_data)
|
||||
return ''
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger()
|
||||
fh = logging.FileHandler('bionicbb.log')
|
||||
fh.setLevel(logging.INFO)
|
||||
logger.addHandler(fh)
|
||||
|
||||
# Prevent the job from being rescheduled by the reloader.
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
||||
scheduler = BackgroundScheduler()
|
||||
scheduler.start()
|
||||
scheduler.add_job(tasks.get_and_process_jobs, 'interval', minutes=5)
|
||||
|
||||
app.run(host='0.0.0.0', debug=True)
|
||||
@@ -29,6 +29,12 @@ def get_commit(change_id, revision):
|
||||
call('/changes/{}/revisions/{}/commit'.format(change_id, revision)))
|
||||
|
||||
|
||||
def get_files_for_revision(change_id, revision):
|
||||
return json.loads(
|
||||
call('/changes/{}/revisions/{}/files'.format(
|
||||
change_id, revision))).keys()
|
||||
|
||||
|
||||
def call(endpoint, method='GET'):
|
||||
if method != 'GET':
|
||||
raise NotImplementedError('Currently only HTTP GET is supported.')
|
||||
|
||||
71
tools/bionicbb/gmail.py
Normal file
71
tools/bionicbb/gmail.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#
|
||||
# Copyright (C) 2015 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import base64
|
||||
import httplib2
|
||||
|
||||
import config
|
||||
|
||||
|
||||
def get_body(msg):
|
||||
if 'attachmentId' in msg['payload']['body']:
|
||||
raise NotImplementedError('Handling of messages contained in '
|
||||
'attachments not yet implemented.')
|
||||
b64_body = msg['payload']['body']['data']
|
||||
return base64.urlsafe_b64decode(b64_body.encode('ASCII'))
|
||||
|
||||
|
||||
def build_service():
|
||||
from apiclient.discovery import build
|
||||
from oauth2client.client import flow_from_clientsecrets
|
||||
from oauth2client.file import Storage
|
||||
from oauth2client.tools import run
|
||||
|
||||
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.modify'
|
||||
STORAGE = Storage('oauth.storage')
|
||||
|
||||
# Start the OAuth flow to retrieve credentials
|
||||
flow = flow_from_clientsecrets(config.client_secret_file,
|
||||
scope=OAUTH_SCOPE)
|
||||
http = httplib2.Http()
|
||||
|
||||
# Try to retrieve credentials from storage or run the flow to generate them
|
||||
credentials = STORAGE.get()
|
||||
if credentials is None or credentials.invalid:
|
||||
credentials = run(flow, STORAGE, http=http)
|
||||
|
||||
http = credentials.authorize(http)
|
||||
return build('gmail', 'v1', http=http)
|
||||
|
||||
|
||||
def get_gerrit_label(labels):
|
||||
for label in labels:
|
||||
if label['name'] == 'gerrit':
|
||||
return label['id']
|
||||
return None
|
||||
|
||||
|
||||
def get_all_messages(service, label):
|
||||
msgs = []
|
||||
response = service.users().messages().list(
|
||||
userId='me', labelIds=label).execute()
|
||||
if 'messages' in response:
|
||||
msgs.extend(response['messages'])
|
||||
while 'nextPageToken' in response:
|
||||
page_token = response['nextPageToken']
|
||||
response = service.users().messages().list(
|
||||
userId='me', pageToken=page_token).execute()
|
||||
msgs.extend(response['messages'])
|
||||
return msgs
|
||||
@@ -1,365 +0,0 @@
|
||||
#!/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, lunch_target=None):
|
||||
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 lunch_target is not None:
|
||||
params['LUNCH_TARGET'] = lunch_target
|
||||
if not dry_run:
|
||||
_ = jenkins[build].invoke(build_params=params)
|
||||
# https://issues.jenkins-ci.org/browse/JENKINS-27256
|
||||
# url = job.get_build().baseurl
|
||||
url = 'URL UNAVAILABLE'
|
||||
else:
|
||||
url = 'DRY_RUN_URL'
|
||||
print '{}({}): {} => {} {} {}'.format(
|
||||
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),
|
||||
|
||||
'arm': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_arm-eng'),
|
||||
'aarch64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_arm64-eng'),
|
||||
'mips': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_mips-eng'),
|
||||
'mips64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_mips64-eng'),
|
||||
'x86': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_x86-eng'),
|
||||
'x86_64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_x86_64-eng'),
|
||||
}
|
||||
|
||||
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
|
||||
except gerrit.GerritError as ex:
|
||||
if ex.code == 404:
|
||||
print '{}(404): {}!'.format(
|
||||
termcolor.colored('ERROR', 'red'), ex)
|
||||
return True
|
||||
else:
|
||||
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)
|
||||
203
tools/bionicbb/presubmit.py
Normal file
203
tools/bionicbb/presubmit.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os.path
|
||||
import re
|
||||
import requests
|
||||
|
||||
import jenkinsapi
|
||||
|
||||
import gerrit
|
||||
|
||||
import config
|
||||
|
||||
|
||||
def is_untrusted_committer(change_id, patch_set):
|
||||
# TODO(danalbert): Needs to be based on the account that made the comment.
|
||||
commit = gerrit.get_commit(change_id, patch_set)
|
||||
committer = commit['committer']['email']
|
||||
return not committer.endswith('@google.com')
|
||||
|
||||
|
||||
def contains_cleanspec(change_id, patch_set):
|
||||
files = gerrit.get_files_for_revision(change_id, patch_set)
|
||||
return 'CleanSpec.mk' in [os.path.basename(f) for f in files]
|
||||
|
||||
|
||||
def contains_bionicbb(change_id, patch_set):
|
||||
files = gerrit.get_files_for_revision(change_id, patch_set)
|
||||
return any('tools/bionicbb' in f for f in files)
|
||||
|
||||
|
||||
def should_skip_build(info):
|
||||
if info['MessageType'] not in ('newchange', 'newpatchset', 'comment'):
|
||||
raise ValueError('should_skip_build() is only valid for new '
|
||||
'changes, patch sets, and commits.')
|
||||
|
||||
change_id = info['Change-Id']
|
||||
patch_set = info['PatchSet']
|
||||
|
||||
checks = [
|
||||
is_untrusted_committer,
|
||||
contains_cleanspec,
|
||||
contains_bionicbb,
|
||||
]
|
||||
for check in checks:
|
||||
if check(change_id, patch_set):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def clean_project(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'
|
||||
logging.info('Cleaning: %s %s', build, url)
|
||||
else:
|
||||
logging.error('Failed to clean: could not find project %s', build)
|
||||
return True
|
||||
|
||||
|
||||
def build_project(gerrit_info, dry_run, lunch_target=None):
|
||||
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/'):
|
||||
raise RuntimeError('Bad project mapping: {} => {}'.format(
|
||||
project, project_path))
|
||||
ref = gerrit.ref_for_change(change_id)
|
||||
params = {
|
||||
'REF': ref,
|
||||
'CHANGE_ID': change_id,
|
||||
'PROJECT': project_path
|
||||
}
|
||||
if lunch_target is not None:
|
||||
params['LUNCH_TARGET'] = lunch_target
|
||||
if not dry_run:
|
||||
_ = jenkins[build].invoke(build_params=params)
|
||||
# https://issues.jenkins-ci.org/browse/JENKINS-27256
|
||||
# url = job.get_build().baseurl
|
||||
url = 'URL UNAVAILABLE'
|
||||
else:
|
||||
url = 'DRY_RUN_URL'
|
||||
logging.info('Building: %s => %s %s %s', project, build, url,
|
||||
change_id)
|
||||
else:
|
||||
logging.error('Unknown build: %s => %s %s', project, build, change_id)
|
||||
return True
|
||||
|
||||
|
||||
def handle_change(gerrit_info, _, dry_run):
|
||||
if should_skip_build(gerrit_info):
|
||||
return True
|
||||
return build_project(gerrit_info, dry_run)
|
||||
|
||||
|
||||
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:
|
||||
logging.error('Failed to drop rejection: %s', ex)
|
||||
return False
|
||||
logging.info('Dropped rejection: %s', 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)
|
||||
|
||||
if should_skip_build(gerrit_info):
|
||||
return True
|
||||
|
||||
command_map = {
|
||||
'clean': lambda: clean_project(dry_run),
|
||||
'retry': lambda: build_project(gerrit_info, dry_run),
|
||||
|
||||
'arm': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_arm-eng'),
|
||||
'aarch64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_arm64-eng'),
|
||||
'mips': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_mips-eng'),
|
||||
'mips64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_mips64-eng'),
|
||||
'x86': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_x86-eng'),
|
||||
'x86_64': lambda: build_project(gerrit_info, dry_run,
|
||||
lunch_target='aosp_x86_64-eng'),
|
||||
}
|
||||
|
||||
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, _, __):
|
||||
logging.info('Skipping %s: %s', gerrit_info['MessageType'],
|
||||
gerrit_info['Change-Id'])
|
||||
return True
|
||||
108
tools/bionicbb/tasks.py
Normal file
108
tools/bionicbb/tasks.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#
|
||||
# Copyright (C) 2015 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import httplib
|
||||
import httplib2
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
|
||||
import apiclient.errors
|
||||
|
||||
import gerrit
|
||||
import gmail
|
||||
import presubmit
|
||||
|
||||
|
||||
def get_gerrit_info(body):
|
||||
info = {}
|
||||
gerrit_pattern = r'^Gerrit-(\S+): (.+)$'
|
||||
for match in re.finditer(gerrit_pattern, body, flags=re.MULTILINE):
|
||||
info[match.group(1)] = match.group(2).strip()
|
||||
return info
|
||||
|
||||
|
||||
def process_message(msg, dry_run):
|
||||
try:
|
||||
body = gmail.get_body(msg)
|
||||
gerrit_info = get_gerrit_info(body)
|
||||
if not gerrit_info:
|
||||
logging.fatal('No Gerrit info found: %s', msg.subject)
|
||||
msg_type = gerrit_info['MessageType']
|
||||
handlers = {
|
||||
'comment': presubmit.handle_comment,
|
||||
'newchange': presubmit.handle_change,
|
||||
'newpatchset': presubmit.handle_change,
|
||||
|
||||
'abandon': presubmit.skip_handler,
|
||||
'merge-failed': presubmit.skip_handler,
|
||||
'merged': presubmit.skip_handler,
|
||||
'restore': presubmit.skip_handler,
|
||||
'revert': presubmit.skip_handler,
|
||||
}
|
||||
|
||||
message_type = gerrit_info['MessageType']
|
||||
if message_type in handlers:
|
||||
return handlers[message_type](gerrit_info, body, dry_run)
|
||||
else:
|
||||
logging.warning('MessageType %s unhandled.', msg_type)
|
||||
return False
|
||||
except NotImplementedError as ex:
|
||||
logging.error("%s", ex)
|
||||
return False
|
||||
except gerrit.GerritError as ex:
|
||||
change_id = gerrit_info['Change-Id']
|
||||
logging.error('Gerrit error (%d): %s %s', ex.code, change_id, ex.url)
|
||||
return ex.code == 404
|
||||
|
||||
|
||||
def get_and_process_jobs():
|
||||
dry_run = False
|
||||
|
||||
gmail_service = gmail.build_service()
|
||||
msg_service = gmail_service.users().messages()
|
||||
|
||||
# We run in a loop because some of the exceptions thrown here mean we just
|
||||
# need to retry. For errors where we should back off (typically any gmail
|
||||
# API exceptions), process_changes catches the error and returns normally.
|
||||
while True:
|
||||
try:
|
||||
process_changes(gmail_service, msg_service, dry_run)
|
||||
return
|
||||
except httplib.BadStatusLine:
|
||||
pass
|
||||
except httplib2.ServerNotFoundError:
|
||||
pass
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
|
||||
def process_changes(gmail_service, msg_service, dry_run):
|
||||
try:
|
||||
labels = gmail_service.users().labels().list(userId='me').execute()
|
||||
if not labels['labels']:
|
||||
logging.error('Could not retrieve Gmail labels')
|
||||
return
|
||||
label_id = gmail.get_gerrit_label(labels['labels'])
|
||||
if not label_id:
|
||||
logging.error('Could not find gerrit label')
|
||||
return
|
||||
|
||||
for msg in gmail.get_all_messages(gmail_service, label_id):
|
||||
msg = msg_service.get(userId='me', id=msg['id']).execute()
|
||||
if process_message(msg, dry_run) and not dry_run:
|
||||
msg_service.trash(userId='me', id=msg['id']).execute()
|
||||
except apiclient.errors.HttpError as ex:
|
||||
logging.error('API Client HTTP error: %s', ex)
|
||||
@@ -1,64 +0,0 @@
|
||||
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()
|
||||
94
tools/bionicbb/test_tasks.py
Normal file
94
tools/bionicbb/test_tasks.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
import presubmit
|
||||
|
||||
|
||||
class TestShouldSkipBuild(unittest.TestCase):
|
||||
@mock.patch('presubmit.contains_bionicbb')
|
||||
@mock.patch('presubmit.contains_cleanspec')
|
||||
@mock.patch('gerrit.get_commit')
|
||||
def test_accepts_googlers(self, mock_commit, *other_checks):
|
||||
mock_commit.return_value = {
|
||||
'committer': {'email': 'googler@google.com'}
|
||||
}
|
||||
|
||||
for other_check in other_checks:
|
||||
other_check.return_value = False
|
||||
|
||||
for message_type in ('newchange', 'newpatchset', 'comment'):
|
||||
self.assertFalse(presubmit.should_skip_build({
|
||||
'MessageType': message_type,
|
||||
'Change-Id': '',
|
||||
'PatchSet': '',
|
||||
}))
|
||||
|
||||
@mock.patch('presubmit.contains_bionicbb')
|
||||
@mock.patch('presubmit.contains_cleanspec')
|
||||
@mock.patch('gerrit.get_commit')
|
||||
def test_rejects_googlish_domains(self, mock_commit, *other_checks):
|
||||
mock_commit.return_value = {
|
||||
'committer': {'email': 'fakegoogler@google.com.fake.com'}
|
||||
}
|
||||
|
||||
for other_check in other_checks:
|
||||
other_check.return_value = False
|
||||
|
||||
for message_type in ('newchange', 'newpatchset', 'comment'):
|
||||
self.assertTrue(presubmit.should_skip_build({
|
||||
'MessageType': message_type,
|
||||
'Change-Id': '',
|
||||
'PatchSet': '',
|
||||
}))
|
||||
|
||||
@mock.patch('presubmit.contains_bionicbb')
|
||||
@mock.patch('presubmit.contains_cleanspec')
|
||||
@mock.patch('gerrit.get_commit')
|
||||
def test_rejects_non_googlers(self, mock_commit, *other_checks):
|
||||
mock_commit.return_value = {
|
||||
'committer': {'email': 'johndoe@example.com'}
|
||||
}
|
||||
|
||||
for other_check in other_checks:
|
||||
other_check.return_value = False
|
||||
|
||||
for message_type in ('newchange', 'newpatchset', 'comment'):
|
||||
self.assertTrue(presubmit.should_skip_build({
|
||||
'MessageType': message_type,
|
||||
'Change-Id': '',
|
||||
'PatchSet': '',
|
||||
}))
|
||||
|
||||
@mock.patch('presubmit.contains_bionicbb')
|
||||
@mock.patch('presubmit.is_untrusted_committer')
|
||||
@mock.patch('gerrit.get_files_for_revision')
|
||||
def test_skips_cleanspecs(self, mock_files, *other_checks):
|
||||
mock_files.return_value = ['foo/CleanSpec.mk']
|
||||
for other_check in other_checks:
|
||||
other_check.return_value = False
|
||||
|
||||
for message_type in ('newchange', 'newpatchset', 'comment'):
|
||||
self.assertTrue(presubmit.should_skip_build({
|
||||
'MessageType': message_type,
|
||||
'Change-Id': '',
|
||||
'PatchSet': '',
|
||||
}))
|
||||
|
||||
@mock.patch('presubmit.contains_cleanspec')
|
||||
@mock.patch('presubmit.is_untrusted_committer')
|
||||
@mock.patch('gerrit.get_files_for_revision')
|
||||
def test_skips_bionicbb(self, mock_files, *other_checks):
|
||||
mock_files.return_value = ['tools/bionicbb/common.sh']
|
||||
for other_check in other_checks:
|
||||
other_check.return_value = False
|
||||
|
||||
for message_type in ('newchange', 'newpatchset', 'comment'):
|
||||
self.assertTrue(presubmit.should_skip_build({
|
||||
'MessageType': message_type,
|
||||
'Change-Id': '',
|
||||
'PatchSet': '',
|
||||
}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
119
tools/relocation_packer/Android.bp
Normal file
119
tools/relocation_packer/Android.bp
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
common_cppflags = [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
"-Wold-style-cast",
|
||||
]
|
||||
|
||||
cc_library_host_static {
|
||||
|
||||
srcs: [
|
||||
"src/debug.cc",
|
||||
"src/delta_encoder.cc",
|
||||
"src/elf_file.cc",
|
||||
"src/packer.cc",
|
||||
"src/sleb128.cc",
|
||||
],
|
||||
|
||||
static_libs: ["libelf"],
|
||||
include_dirs: ["external/elfutils/src/libelf"],
|
||||
name: "lib_relocation_packer",
|
||||
|
||||
cppflags: common_cppflags,
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
disabled: true
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
cc_binary_host {
|
||||
|
||||
srcs: ["src/main.cc"],
|
||||
static_libs: [
|
||||
"lib_relocation_packer",
|
||||
"libelf",
|
||||
],
|
||||
stl: "libc++_static",
|
||||
include_dirs: [
|
||||
"external/elfutils/src/libelf",
|
||||
"libnativehelper/include",
|
||||
],
|
||||
|
||||
name: "relocation_packer",
|
||||
|
||||
cppflags: common_cppflags,
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
disabled: true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
|
||||
srcs: [
|
||||
"src/debug_unittest.cc",
|
||||
"src/delta_encoder_unittest.cc",
|
||||
"src/elf_file_unittest.cc",
|
||||
"src/sleb128_unittest.cc",
|
||||
"src/packer_unittest.cc",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"lib_relocation_packer",
|
||||
"libelf",
|
||||
],
|
||||
include_dirs: ["external/elfutils/src/libelf"],
|
||||
|
||||
cppflags: common_cppflags,
|
||||
|
||||
name: "relocation_packer_unit_tests",
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
disabled: true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// $(1) library name
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported directive
|
||||
// define copy-test-library
|
||||
// include $(CLEAR_VARS)
|
||||
// LOCAL_IS_HOST_MODULE := true
|
||||
// LOCAL_MODULE := $(1)
|
||||
// LOCAL_MODULE_CLASS := SHARED_LIBRARIES
|
||||
// LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
|
||||
// LOCAL_STRIP_MODULE := false
|
||||
// LOCAL_SRC_FILES := test_data/$(1)
|
||||
// include $(BUILD_PREBUILT)
|
||||
//
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported line
|
||||
// $(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported line
|
||||
// $(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported line
|
||||
// $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported line
|
||||
// $(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))
|
||||
@@ -26,7 +26,6 @@ LOCAL_SRC_FILES := \
|
||||
src/debug.cc \
|
||||
src/delta_encoder.cc \
|
||||
src/elf_file.cc \
|
||||
src/leb128.cc \
|
||||
src/packer.cc \
|
||||
src/sleb128.cc \
|
||||
|
||||
@@ -46,6 +45,9 @@ LOCAL_CPP_EXTENSION := .cc
|
||||
|
||||
LOCAL_SRC_FILES := src/main.cc
|
||||
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
|
||||
|
||||
# Statically linking libc++ to make it work from prebuilts
|
||||
LOCAL_CXX_STL := libc++_static
|
||||
LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
|
||||
|
||||
LOCAL_MODULE := relocation_packer
|
||||
@@ -64,7 +66,6 @@ LOCAL_SRC_FILES := \
|
||||
src/debug_unittest.cc \
|
||||
src/delta_encoder_unittest.cc \
|
||||
src/elf_file_unittest.cc \
|
||||
src/leb128_unittest.cc \
|
||||
src/sleb128_unittest.cc \
|
||||
src/packer_unittest.cc \
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user