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/sprintf.c \
|
||||||
stdio/stdio.c \
|
stdio/stdio.c \
|
||||||
stdio/stdio_ext.cpp \
|
stdio/stdio_ext.cpp \
|
||||||
|
stdlib/atexit.c \
|
||||||
|
stdlib/exit.c \
|
||||||
|
|
||||||
# Fortify implementations of libc functions.
|
# Fortify implementations of libc functions.
|
||||||
libc_common_src_files += \
|
libc_common_src_files += \
|
||||||
bionic/__FD_chk.cpp \
|
bionic/__FD_chk.cpp \
|
||||||
bionic/__fgets_chk.cpp \
|
bionic/__fgets_chk.cpp \
|
||||||
|
bionic/__memchr_chk.cpp \
|
||||||
bionic/__memmove_chk.cpp \
|
bionic/__memmove_chk.cpp \
|
||||||
|
bionic/__memrchr_chk.cpp \
|
||||||
bionic/__poll_chk.cpp \
|
bionic/__poll_chk.cpp \
|
||||||
|
bionic/__pread64_chk.cpp \
|
||||||
|
bionic/__pread_chk.cpp \
|
||||||
bionic/__read_chk.cpp \
|
bionic/__read_chk.cpp \
|
||||||
|
bionic/__readlink_chk.cpp \
|
||||||
|
bionic/__readlinkat_chk.cpp \
|
||||||
bionic/__recvfrom_chk.cpp \
|
bionic/__recvfrom_chk.cpp \
|
||||||
bionic/__stpcpy_chk.cpp \
|
bionic/__stpcpy_chk.cpp \
|
||||||
bionic/__stpncpy_chk.cpp \
|
bionic/__stpncpy_chk.cpp \
|
||||||
@@ -104,6 +112,7 @@ libc_bionic_ndk_src_files := \
|
|||||||
bionic/clock_getcpuclockid.cpp \
|
bionic/clock_getcpuclockid.cpp \
|
||||||
bionic/clock_nanosleep.cpp \
|
bionic/clock_nanosleep.cpp \
|
||||||
bionic/clone.cpp \
|
bionic/clone.cpp \
|
||||||
|
bionic/close.cpp \
|
||||||
bionic/__cmsg_nxthdr.cpp \
|
bionic/__cmsg_nxthdr.cpp \
|
||||||
bionic/connect.cpp \
|
bionic/connect.cpp \
|
||||||
bionic/ctype.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/wprintf.c \
|
||||||
upstream-openbsd/lib/libc/stdio/wscanf.c \
|
upstream-openbsd/lib/libc/stdio/wscanf.c \
|
||||||
upstream-openbsd/lib/libc/stdio/wsetup.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/atoi.c \
|
||||||
upstream-openbsd/lib/libc/stdlib/atol.c \
|
upstream-openbsd/lib/libc/stdlib/atol.c \
|
||||||
upstream-openbsd/lib/libc/stdlib/atoll.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/getenv.c \
|
||||||
upstream-openbsd/lib/libc/stdlib/insque.c \
|
upstream-openbsd/lib/libc/stdlib/insque.c \
|
||||||
upstream-openbsd/lib/libc/stdlib/lsearch.c \
|
upstream-openbsd/lib/libc/stdlib/lsearch.c \
|
||||||
@@ -820,12 +827,7 @@ include $(BUILD_STATIC_LIBRARY)
|
|||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(libc_upstream_openbsd_ndk_src_files)
|
LOCAL_SRC_FILES := $(libc_upstream_openbsd_ndk_src_files)
|
||||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
LOCAL_CLANG := $(use_clang)
|
||||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
|
||||||
LOCAL_CLANG := false
|
|
||||||
else
|
|
||||||
LOCAL_CLANG := $(use_clang)
|
|
||||||
endif
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
$(libc_common_cflags) \
|
$(libc_common_cflags) \
|
||||||
@@ -863,12 +865,7 @@ include $(BUILD_STATIC_LIBRARY)
|
|||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(libc_upstream_openbsd_src_files)
|
LOCAL_SRC_FILES := $(libc_upstream_openbsd_src_files)
|
||||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
LOCAL_CLANG := $(use_clang)
|
||||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
|
||||||
LOCAL_CLANG := false
|
|
||||||
else
|
|
||||||
LOCAL_CLANG := $(use_clang)
|
|
||||||
endif
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
$(libc_common_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_32 := $(libc_upstream_openbsd_gdtoa_src_files_32)
|
||||||
LOCAL_SRC_FILES_64 := $(libc_upstream_openbsd_gdtoa_src_files_64)
|
LOCAL_SRC_FILES_64 := $(libc_upstream_openbsd_gdtoa_src_files_64)
|
||||||
ifneq (,$(filter $(TARGET_ARCH),x86 x86_64))
|
LOCAL_CLANG := $(use_clang)
|
||||||
# Clang has wrong long double size or LDBL_MANT_DIG, http://b/17163651.
|
|
||||||
LOCAL_CLANG := false
|
|
||||||
else
|
|
||||||
LOCAL_CLANG := $(use_clang)
|
|
||||||
endif
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
$(libc_common_cflags) \
|
$(libc_common_cflags) \
|
||||||
@@ -949,9 +941,6 @@ LOCAL_SRC_FILES := $(libc_bionic_src_files)
|
|||||||
LOCAL_CFLAGS := $(libc_common_cflags) \
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
-Wframe-larger-than=2048 \
|
-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.
|
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||||
LOCAL_CLANG_ASFLAGS_arm64 += -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) \
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
-Wframe-larger-than=2048 \
|
-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.
|
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||||
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
|
||||||
@@ -1007,7 +993,7 @@ include $(BUILD_STATIC_LIBRARY)
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files)
|
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_CONLYFLAGS := $(libc_common_conlyflags)
|
||||||
LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
|
LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
|
||||||
@@ -1038,9 +1024,6 @@ LOCAL_SRC_FILES := $(libc_pthread_src_files)
|
|||||||
LOCAL_CFLAGS := $(libc_common_cflags) \
|
LOCAL_CFLAGS := $(libc_common_cflags) \
|
||||||
-Wframe-larger-than=2048 \
|
-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.
|
# memcpy.S, memchr.S, etc. do not compile with Clang.
|
||||||
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
|
||||||
LOCAL_CLANG_ASFLAGS_arm64 += -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
|
LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi
|
||||||
|
|
||||||
ifneq ($(MALLOC_IMPL),dlmalloc)
|
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
|
||||||
endif
|
|
||||||
|
|
||||||
LOCAL_CXX_STL := none
|
LOCAL_CXX_STL := none
|
||||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
|
|
||||||
@@ -1328,6 +1307,11 @@ LOCAL_MODULE := libc
|
|||||||
LOCAL_CLANG := $(use_clang)
|
LOCAL_CLANG := $(use_clang)
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
||||||
|
|
||||||
|
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||||
|
endif
|
||||||
|
|
||||||
LOCAL_CXX_STL := none
|
LOCAL_CXX_STL := none
|
||||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
||||||
LOCAL_ADDRESS_SANITIZER := false
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
@@ -1349,10 +1333,13 @@ LOCAL_CPPFLAGS := $(libc_common_cppflags)
|
|||||||
|
|
||||||
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
|
arch-common/bionic/crtbegin_so.c \
|
||||||
|
arch-common/bionic/crtbrand.S \
|
||||||
$(libc_arch_dynamic_src_files) \
|
$(libc_arch_dynamic_src_files) \
|
||||||
bionic/malloc_debug_common.cpp \
|
bionic/malloc_debug_common.cpp \
|
||||||
bionic/libc_init_dynamic.cpp \
|
bionic/libc_init_dynamic.cpp \
|
||||||
bionic/NetdClient.cpp \
|
bionic/NetdClient.cpp \
|
||||||
|
arch-common/bionic/crtend_so.S \
|
||||||
|
|
||||||
LOCAL_MODULE := libc
|
LOCAL_MODULE := libc
|
||||||
LOCAL_CLANG := $(use_clang)
|
LOCAL_CLANG := $(use_clang)
|
||||||
@@ -1365,6 +1352,9 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \
|
|||||||
# meaningful name resolution.
|
# meaningful name resolution.
|
||||||
LOCAL_STRIP_MODULE := keep_symbols
|
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,
|
# 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
|
# 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
|
# 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_SHARED_LIBRARIES := libdl
|
||||||
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
LOCAL_WHOLE_STATIC_LIBRARIES := libc_common
|
||||||
|
|
||||||
|
ifneq ($(MALLOC_IMPL),dlmalloc)
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES += libjemalloc
|
||||||
|
endif
|
||||||
|
|
||||||
LOCAL_CXX_STL := none
|
LOCAL_CXX_STL := none
|
||||||
LOCAL_SYSTEM_SHARED_LIBRARIES :=
|
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_CFLAGS,libc_common_cflags))
|
||||||
$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files))
|
$(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
|
# special for arm
|
||||||
LOCAL_NO_CRT_arm := true
|
|
||||||
LOCAL_CFLAGS_arm += -DCRT_LEGACY_WORKAROUND
|
LOCAL_CFLAGS_arm += -DCRT_LEGACY_WORKAROUND
|
||||||
LOCAL_ASFLAGS_arm += $(libc_crt_target_cflags)
|
|
||||||
LOCAL_SRC_FILES_arm += \
|
LOCAL_SRC_FILES_arm += \
|
||||||
arch-common/bionic/crtbegin_so.c \
|
arch-arm/bionic/atexit_legacy.c
|
||||||
arch-common/bionic/crtbrand.S \
|
|
||||||
arch-arm/bionic/atexit_legacy.c \
|
|
||||||
arch-common/bionic/crtend_so.S
|
|
||||||
LOCAL_ADDRESS_SANITIZER := false
|
LOCAL_ADDRESS_SANITIZER := false
|
||||||
LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
|
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 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(int, void*, size_t, off64_t) arm,mips,x86
|
||||||
ssize_t pwrite64|pwrite(int, void*, size_t, off_t) arm64,mips64,x86_64
|
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
|
pid_t __getpid:getpid() all
|
||||||
int munmap(void*, size_t) all
|
int munmap(void*, size_t) all
|
||||||
void* mremap(void*, size_t, size_t, unsigned long) all
|
void* mremap(void*, size_t, size_t, unsigned long) all
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
libc_bionic_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
arch-arm/generic/bionic/memcmp.S \
|
|
||||||
arch-arm/cortex-a15/bionic/memcpy.S \
|
arch-arm/cortex-a15/bionic/memcpy.S \
|
||||||
arch-arm/cortex-a15/bionic/memset.S \
|
arch-arm/cortex-a15/bionic/memset.S \
|
||||||
arch-arm/cortex-a15/bionic/stpcpy.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/__strcpy_chk.S \
|
||||||
arch-arm/cortex-a15/bionic/strlen.S \
|
arch-arm/cortex-a15/bionic/strlen.S \
|
||||||
|
|
||||||
libc_openbsd_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
upstream-openbsd/lib/libc/string/memmove.c \
|
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 */
|
/* check if buffers are aligned. If so, run arm-only version */
|
||||||
eor r3, r0, r1
|
eor r3, r0, r1
|
||||||
ands r3, r3, #0x3
|
ands r3, r3, #0x3
|
||||||
beq __memcpy_base_aligned
|
beq MEMCPY_BASE_ALIGNED
|
||||||
|
|
||||||
/* Check the upper size limit for Neon unaligned memory access in memcpy */
|
/* Check the upper size limit for Neon unaligned memory access in memcpy */
|
||||||
cmp r2, #224
|
cmp r2, #224
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
libc_bionic_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
arch-arm/generic/bionic/memcmp.S \
|
|
||||||
arch-arm/cortex-a9/bionic/memcpy.S \
|
arch-arm/cortex-a9/bionic/memcpy.S \
|
||||||
arch-arm/cortex-a9/bionic/memset.S \
|
arch-arm/cortex-a9/bionic/memset.S \
|
||||||
arch-arm/cortex-a9/bionic/stpcpy.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/__strcpy_chk.S \
|
||||||
arch-arm/cortex-a9/bionic/strlen.S \
|
arch-arm/cortex-a9/bionic/strlen.S \
|
||||||
|
|
||||||
libc_openbsd_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
upstream-openbsd/lib/libc/string/memmove.c \
|
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 += \
|
libc_bionic_src_files_arm += \
|
||||||
arch-arm/generic/bionic/memcmp.S \
|
|
||||||
arch-arm/krait/bionic/memcpy.S \
|
arch-arm/krait/bionic/memcpy.S \
|
||||||
arch-arm/krait/bionic/memset.S \
|
arch-arm/krait/bionic/memset.S \
|
||||||
arch-arm/krait/bionic/strcmp.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/strcpy.S \
|
||||||
arch-arm/cortex-a15/bionic/strlen.S \
|
arch-arm/cortex-a15/bionic/strlen.S \
|
||||||
|
|
||||||
libc_openbsd_src_files_arm += \
|
libc_bionic_src_files_arm += \
|
||||||
upstream-openbsd/lib/libc/string/memmove.c \
|
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>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
mov ip, r7
|
mov ip, r7
|
||||||
ldr r7, =__NR_close
|
ldr r7, =__NR_close
|
||||||
swi #0
|
swi #0
|
||||||
@@ -11,4 +11,5 @@ ENTRY(close)
|
|||||||
bxls lr
|
bxls lr
|
||||||
neg r0, r0
|
neg r0, r0
|
||||||
b __set_errno_internal
|
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/__dso_handle.h"
|
||||||
#include "../../arch-common/bionic/atexit.h"
|
#include "../../arch-common/bionic/atexit.h"
|
||||||
|
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
mov x8, __NR_close
|
mov x8, __NR_close
|
||||||
svc #0
|
svc #0
|
||||||
|
|
||||||
@@ -11,4 +11,5 @@ ENTRY(close)
|
|||||||
b.hi __set_errno_internal
|
b.hi __set_errno_internal
|
||||||
|
|
||||||
ret
|
ret
|
||||||
END(close)
|
END(___close)
|
||||||
|
.hidden ___close
|
||||||
@@ -59,6 +59,7 @@ void _start() {
|
|||||||
|
|
||||||
#include "__dso_handle.h"
|
#include "__dso_handle.h"
|
||||||
#include "atexit.h"
|
#include "atexit.h"
|
||||||
|
#include "pthread_atfork.h"
|
||||||
#ifdef __i386__
|
#ifdef __i386__
|
||||||
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ void __on_dlclose() {
|
|||||||
# include "__dso_handle_so.h"
|
# include "__dso_handle_so.h"
|
||||||
# include "atexit.h"
|
# include "atexit.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "pthread_atfork.h"
|
||||||
#ifdef __i386__
|
#ifdef __i386__
|
||||||
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
# include "../../arch-x86/bionic/__stack_chk_fail_local.h"
|
||||||
#endif
|
#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/__dso_handle.h"
|
||||||
#include "../../arch-common/bionic/atexit.h"
|
#include "../../arch-common/bionic/atexit.h"
|
||||||
|
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
.cpload t9
|
.cpload t9
|
||||||
li v0, __NR_close
|
li v0, __NR_close
|
||||||
@@ -16,4 +16,5 @@ ENTRY(close)
|
|||||||
j t9
|
j t9
|
||||||
nop
|
nop
|
||||||
.set reorder
|
.set reorder
|
||||||
END(close)
|
END(___close)
|
||||||
|
.hidden ___close
|
||||||
@@ -92,3 +92,4 @@ __asm__ (
|
|||||||
|
|
||||||
#include "../../arch-common/bionic/__dso_handle.h"
|
#include "../../arch-common/bionic/__dso_handle.h"
|
||||||
#include "../../arch-common/bionic/atexit.h"
|
#include "../../arch-common/bionic/atexit.h"
|
||||||
|
#include "../../arch-common/bionic/pthread_atfork.h"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
.set push
|
.set push
|
||||||
.set noreorder
|
.set noreorder
|
||||||
li v0, __NR_close
|
li v0, __NR_close
|
||||||
@@ -22,4 +22,5 @@ ENTRY(close)
|
|||||||
j t9
|
j t9
|
||||||
move ra, t0
|
move ra, t0
|
||||||
.set pop
|
.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);
|
// 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)
|
ENTRY(__bionic_clone)
|
||||||
pushl %ebx
|
pushl %ebx
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset ebx, 0
|
||||||
pushl %esi
|
pushl %esi
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset esi, 0
|
||||||
pushl %edi
|
pushl %edi
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset edi, 0
|
||||||
|
|
||||||
# Load system call arguments into registers.
|
# Load system call arguments into registers.
|
||||||
movl 16(%esp), %ebx # flags
|
movl 16(%esp), %ebx # flags
|
||||||
@@ -46,8 +52,14 @@ ENTRY(__bionic_clone)
|
|||||||
# We're the parent; nothing to do.
|
# We're the parent; nothing to do.
|
||||||
.L_bc_return:
|
.L_bc_return:
|
||||||
popl %edi
|
popl %edi
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore edi
|
||||||
popl %esi
|
popl %esi
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore esi
|
||||||
popl %ebx
|
popl %ebx
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore ebx
|
||||||
ret
|
ret
|
||||||
END(__bionic_clone)
|
END(__bionic_clone)
|
||||||
.hidden __bionic_clone
|
.hidden __bionic_clone
|
||||||
|
|||||||
@@ -15,9 +15,17 @@
|
|||||||
ENTRY(syscall)
|
ENTRY(syscall)
|
||||||
# Push the callee save registers.
|
# Push the callee save registers.
|
||||||
push %ebx
|
push %ebx
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset ebx, 0
|
||||||
push %esi
|
push %esi
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset esi, 0
|
||||||
push %edi
|
push %edi
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset edi, 0
|
||||||
push %ebp
|
push %ebp
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset ebp, 0
|
||||||
|
|
||||||
# Load all the arguments from the calling frame.
|
# Load all the arguments from the calling frame.
|
||||||
# (Not all will be valid, depending on the syscall.)
|
# (Not all will be valid, depending on the syscall.)
|
||||||
@@ -43,8 +51,16 @@ ENTRY(syscall)
|
|||||||
1:
|
1:
|
||||||
# Restore the callee save registers.
|
# Restore the callee save registers.
|
||||||
pop %ebp
|
pop %ebp
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore ebp
|
||||||
pop %edi
|
pop %edi
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore edi
|
||||||
pop %esi
|
pop %esi
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore esi
|
||||||
pop %ebx
|
pop %ebx
|
||||||
|
.cfi_adjust_cfa_offset -4
|
||||||
|
.cfi_restore ebx
|
||||||
ret
|
ret
|
||||||
END(syscall)
|
END(syscall)
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
ENTRY(vfork)
|
ENTRY(vfork)
|
||||||
popl %ecx // Grab the return address.
|
popl %ecx // Grab the return address.
|
||||||
|
.cfi_adjust_cfa_offset 4
|
||||||
|
.cfi_rel_offset ecx, 0
|
||||||
movl $__NR_vfork, %eax
|
movl $__NR_vfork, %eax
|
||||||
int $0x80
|
int $0x80
|
||||||
cmpl $-MAX_ERRNO, %eax
|
cmpl $-MAX_ERRNO, %eax
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
pushl %ebx
|
pushl %ebx
|
||||||
.cfi_def_cfa_offset 8
|
.cfi_def_cfa_offset 8
|
||||||
.cfi_rel_offset ebx, 0
|
.cfi_rel_offset ebx, 0
|
||||||
@@ -18,4 +18,5 @@ ENTRY(close)
|
|||||||
1:
|
1:
|
||||||
popl %ebx
|
popl %ebx
|
||||||
ret
|
ret
|
||||||
END(close)
|
END(___close)
|
||||||
|
.hidden ___close
|
||||||
@@ -1897,8 +1897,8 @@ L(strcmp_exitz):
|
|||||||
|
|
||||||
.p2align 4
|
.p2align 4
|
||||||
L(Byte0):
|
L(Byte0):
|
||||||
movzx (%rsi), %ecx
|
movzbl (%rsi), %ecx
|
||||||
movzx (%rdi), %eax
|
movzbl (%rdi), %eax
|
||||||
|
|
||||||
sub %ecx, %eax
|
sub %ecx, %eax
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <private/bionic_asm.h>
|
#include <private/bionic_asm.h>
|
||||||
|
|
||||||
ENTRY(close)
|
ENTRY(___close)
|
||||||
movl $__NR_close, %eax
|
movl $__NR_close, %eax
|
||||||
syscall
|
syscall
|
||||||
cmpq $-MAX_ERRNO, %rax
|
cmpq $-MAX_ERRNO, %rax
|
||||||
@@ -12,4 +12,5 @@ ENTRY(close)
|
|||||||
call __set_errno_internal
|
call __set_errno_internal
|
||||||
1:
|
1:
|
||||||
ret
|
ret
|
||||||
END(close)
|
END(___close)
|
||||||
|
.hidden ___close
|
||||||
@@ -22,7 +22,7 @@ struct thread_local_dtor {
|
|||||||
thread_local_dtor* next;
|
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) {
|
extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
|
||||||
thread_local_dtor* dtor = new thread_local_dtor();
|
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.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#undef _FORTIFY_SOURCE
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ enum AndroidEventLogType {
|
|||||||
EVENT_TYPE_LONG = 1,
|
EVENT_TYPE_LONG = 1,
|
||||||
EVENT_TYPE_STRING = 2,
|
EVENT_TYPE_STRING = 2,
|
||||||
EVENT_TYPE_LIST = 3,
|
EVENT_TYPE_LIST = 3,
|
||||||
|
EVENT_TYPE_FLOAT = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BufferOutputStream {
|
struct BufferOutputStream {
|
||||||
|
|||||||
@@ -26,14 +26,15 @@
|
|||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pthread_internal.h"
|
|
||||||
#include "private/bionic_futex.h"
|
|
||||||
#include "private/kernel_sigset_t.h"
|
#include "private/kernel_sigset_t.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
// System calls.
|
// System calls.
|
||||||
extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
|
extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
|
||||||
@@ -59,11 +60,11 @@ struct PosixTimer {
|
|||||||
|
|
||||||
int sigev_notify;
|
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;
|
pthread_t callback_thread;
|
||||||
void (*callback)(sigval_t);
|
void (*callback)(sigval_t);
|
||||||
sigval_t callback_argument;
|
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) {
|
static __kernel_timer_t to_kernel_timer_id(timer_t timer) {
|
||||||
@@ -85,8 +86,13 @@ static void* __timer_thread_start(void* arg) {
|
|||||||
continue;
|
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.
|
// 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);
|
timer->callback(timer->callback_argument);
|
||||||
} else if (si.si_code == SI_TKILL) {
|
} else if (si.si_code == SI_TKILL) {
|
||||||
// This signal was sent because someone wants us to exit.
|
// 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) {
|
static void __timer_thread_stop(PosixTimer* timer) {
|
||||||
// Immediately mark the timer as disarmed so even if some events
|
atomic_store(&timer->deleted, true);
|
||||||
// continue to happen, the callback won't be called.
|
|
||||||
timer->armed = false;
|
|
||||||
pthread_kill(timer->callback_thread, TIMER_SIGNAL);
|
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...
|
// Otherwise, this must be SIGEV_THREAD timer...
|
||||||
timer->callback = evp->sigev_notify_function;
|
timer->callback = evp->sigev_notify_function;
|
||||||
timer->callback_argument = evp->sigev_value;
|
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.
|
// Check arguments that the kernel doesn't care about but we do.
|
||||||
if (timer->callback == NULL) {
|
if (timer->callback == NULL) {
|
||||||
@@ -170,10 +174,10 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
|
|||||||
return -1;
|
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.
|
// It can't do this itself because the kernel timer isn't created until after it's running.
|
||||||
char name[32];
|
char name[16]; // 16 is the kernel-imposed limit.
|
||||||
snprintf(name, sizeof(name), "POSIX interval timer %d", to_kernel_timer_id(timer));
|
snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer));
|
||||||
pthread_setname_np(timer->callback_thread, name);
|
pthread_setname_np(timer->callback_thread, name);
|
||||||
|
|
||||||
*timer_id = timer;
|
*timer_id = timer;
|
||||||
@@ -199,25 +203,19 @@ int timer_delete(timer_t id) {
|
|||||||
return 0;
|
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) {
|
int timer_gettime(timer_t id, itimerspec* ts) {
|
||||||
return __timer_gettime(to_kernel_timer_id(id), 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) {
|
int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
|
||||||
PosixTimer* timer= reinterpret_cast<PosixTimer*>(id);
|
PosixTimer* timer= reinterpret_cast<PosixTimer*>(id);
|
||||||
int rc = __timer_settime(timer->kernel_timer_id, flags, ts, ots);
|
return __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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
|
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "private/bionic_macros.h"
|
||||||
|
|
||||||
struct atfork_t {
|
struct atfork_t {
|
||||||
atfork_t* next;
|
atfork_t* next;
|
||||||
atfork_t* prev;
|
atfork_t* prev;
|
||||||
@@ -37,79 +39,143 @@ struct atfork_t {
|
|||||||
void (*prepare)(void);
|
void (*prepare)(void);
|
||||||
void (*child)(void);
|
void (*child)(void);
|
||||||
void (*parent)(void);
|
void (*parent)(void);
|
||||||
|
|
||||||
|
void* dso_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct atfork_list_t {
|
class atfork_list_t {
|
||||||
atfork_t* first;
|
public:
|
||||||
atfork_t* last;
|
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 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() {
|
void __bionic_atfork_run_prepare() {
|
||||||
// We lock the atfork list here, unlock it in the parent, and reset it in the child.
|
// 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
|
// This ensures that nobody can modify the handler array between the calls
|
||||||
// to the prepare and parent/child handlers.
|
// 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);
|
pthread_mutex_lock(&g_atfork_list_mutex);
|
||||||
|
|
||||||
// Call pthread_atfork() prepare handlers. POSIX states that the prepare
|
// Call pthread_atfork() prepare handlers. POSIX states that the prepare
|
||||||
// handlers should be called in the reverse order of the parent/child
|
// handlers should be called in the reverse order of the parent/child
|
||||||
// handlers, so we iterate backwards.
|
// handlers, so we iterate backwards.
|
||||||
for (atfork_t* it = g_atfork_list.last; it != NULL; it = it->prev) {
|
g_atfork_list.walk_backwards([](atfork_t* it) {
|
||||||
if (it->prepare != NULL) {
|
if (it->prepare != nullptr) {
|
||||||
it->prepare();
|
it->prepare();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void __bionic_atfork_run_child() {
|
void __bionic_atfork_run_child() {
|
||||||
for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) {
|
g_atfork_list.walk_forward([](atfork_t* it) {
|
||||||
if (it->child != NULL) {
|
if (it->child != nullptr) {
|
||||||
it->child();
|
it->child();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __bionic_atfork_run_parent() {
|
void __bionic_atfork_run_parent() {
|
||||||
for (atfork_t* it = g_atfork_list.first; it != NULL; it = it->next) {
|
g_atfork_list.walk_forward([](atfork_t* it) {
|
||||||
if (it->parent != NULL) {
|
if (it->parent != nullptr) {
|
||||||
it->parent();
|
it->parent();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_atfork_list_mutex);
|
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)));
|
atfork_t* entry = reinterpret_cast<atfork_t*>(malloc(sizeof(atfork_t)));
|
||||||
if (entry == NULL) {
|
if (entry == nullptr) {
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->prepare = prepare;
|
entry->prepare = prepare;
|
||||||
entry->parent = parent;
|
entry->parent = parent;
|
||||||
entry->child = child;
|
entry->child = child;
|
||||||
|
entry->dso_handle = dso;
|
||||||
|
|
||||||
pthread_mutex_lock(&g_atfork_list_mutex);
|
pthread_mutex_lock(&g_atfork_list_mutex);
|
||||||
|
|
||||||
// Append 'entry' to the list.
|
g_atfork_list.push_back(entry);
|
||||||
entry->next = NULL;
|
|
||||||
entry->prev = g_atfork_list.last;
|
|
||||||
if (entry->prev != NULL) {
|
|
||||||
entry->prev->next = entry;
|
|
||||||
}
|
|
||||||
if (g_atfork_list.first == NULL) {
|
|
||||||
g_atfork_list.first = entry;
|
|
||||||
}
|
|
||||||
g_atfork_list.last = entry;
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_atfork_list_mutex);
|
pthread_mutex_unlock(&g_atfork_list_mutex);
|
||||||
|
|
||||||
return 0;
|
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);
|
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) {
|
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
|
// 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)) {
|
while (!SeqOfKeyInUse(seq)) {
|
||||||
if (atomic_compare_exchange_weak(&key_map[i].seq, &seq, seq + SEQ_INCREMENT_STEP)) {
|
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));
|
atomic_store(&key_map[i].key_destructor, reinterpret_cast<uintptr_t>(key_destructor));
|
||||||
*key = i;
|
*key = i | KEY_VALID_FLAG;
|
||||||
return 0;
|
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
|
// responsibility of the caller to properly dispose of the corresponding data
|
||||||
// and resources, using any means it finds suitable.
|
// and resources, using any means it finds suitable.
|
||||||
int pthread_key_delete(pthread_key_t key) {
|
int pthread_key_delete(pthread_key_t key) {
|
||||||
if (!KeyInValidRange(key)) {
|
if (__predict_false(!KeyInValidRange(key))) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
key &= ~KEY_VALID_FLAG;
|
||||||
// Increase seq to invalidate values in all threads.
|
// Increase seq to invalidate values in all threads.
|
||||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||||
if (SeqOfKeyInUse(seq)) {
|
if (SeqOfKeyInUse(seq)) {
|
||||||
@@ -141,9 +149,10 @@ int pthread_key_delete(pthread_key_t key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* pthread_getspecific(pthread_key_t key) {
|
void* pthread_getspecific(pthread_key_t key) {
|
||||||
if (!KeyInValidRange(key)) {
|
if (__predict_false(!KeyInValidRange(key))) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
key &= ~KEY_VALID_FLAG;
|
||||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
||||||
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
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,
|
// 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)) {
|
if (__predict_true(SeqOfKeyInUse(seq) && data->seq == seq)) {
|
||||||
return data->data;
|
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;
|
data->data = NULL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
int pthread_setspecific(pthread_key_t key, const void* ptr) {
|
||||||
if (!KeyInValidRange(key)) {
|
if (__predict_false(!KeyInValidRange(key))) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
key &= ~KEY_VALID_FLAG;
|
||||||
uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
|
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]);
|
pthread_key_data_t* data = &(__get_thread()->key_data[key]);
|
||||||
data->seq = seq;
|
data->seq = seq;
|
||||||
data->data = const_cast<void*>(ptr);
|
data->data = const_cast<void*>(ptr);
|
||||||
|
|||||||
@@ -28,9 +28,11 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "pthread_internal.h"
|
#include "pthread_internal.h"
|
||||||
#include "private/bionic_futex.h"
|
#include "private/bionic_futex.h"
|
||||||
|
#include "private/bionic_lock.h"
|
||||||
#include "private/bionic_time_conversions.h"
|
#include "private/bionic_time_conversions.h"
|
||||||
|
|
||||||
/* Technical note:
|
/* Technical note:
|
||||||
@@ -53,18 +55,39 @@
|
|||||||
* - This implementation will return EDEADLK in "write after write" and "read after
|
* - This implementation will return EDEADLK in "write after write" and "read after
|
||||||
* write" cases and will deadlock in write after read case.
|
* 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
|
// A rwlockattr is implemented as a 32-bit integer which has following fields:
|
||||||
#define RWLOCKATTR_SHARED_MASK 0x0010
|
// 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) {
|
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr) {
|
||||||
*attr = PTHREAD_PROCESS_PRIVATE;
|
*attr = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,40 +96,121 @@ int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr) {
|
|||||||
return 0;
|
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) {
|
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared) {
|
||||||
switch (pshared) {
|
switch (pshared) {
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
case PTHREAD_PROCESS_PRIVATE:
|
||||||
|
__rwlockattr_setpshared(attr, 0);
|
||||||
|
return 0;
|
||||||
case PTHREAD_PROCESS_SHARED:
|
case PTHREAD_PROCESS_SHARED:
|
||||||
*attr = pshared;
|
__rwlockattr_setpshared(attr, 1);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) {
|
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t* attr, int* pref) {
|
||||||
*pshared = *attr;
|
*pref = __rwlockattr_getkind(attr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pthread_rwlock_internal_t {
|
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref) {
|
||||||
atomic_int state; // 0=unlock, -1=writer lock, +n=reader lock
|
switch (pref) {
|
||||||
atomic_int writer_thread_id;
|
case PTHREAD_RWLOCK_PREFER_READER_NP: // Fall through.
|
||||||
atomic_uint pending_readers;
|
case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
|
||||||
atomic_uint pending_writers;
|
__rwlockattr_setkind(attr, pref);
|
||||||
int32_t attr;
|
return 0;
|
||||||
|
default:
|
||||||
bool process_shared() const {
|
return EINVAL;
|
||||||
return attr == PTHREAD_PROCESS_SHARED;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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__)
|
#if defined(__LP64__)
|
||||||
char __reserved[36];
|
|
||||||
#else
|
|
||||||
char __reserved[20];
|
char __reserved[20];
|
||||||
|
#else
|
||||||
|
char __reserved[4];
|
||||||
#endif
|
#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),
|
static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t),
|
||||||
"pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation.");
|
"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,
|
static_assert(alignof(pthread_rwlock_t) == 4,
|
||||||
"pthread_rwlock_t should fulfill the alignment requirement of pthread_rwlock_internal_t.");
|
"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);
|
return reinterpret_cast<pthread_rwlock_internal_t*>(rwlock_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_init(pthread_rwlock_t* rwlock_interface, const pthread_rwlockattr_t* attr) {
|
int pthread_rwlock_init(pthread_rwlock_t* rwlock_interface, const pthread_rwlockattr_t* attr) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
if (__predict_true(attr == NULL)) {
|
memset(rwlock, 0, sizeof(pthread_rwlock_internal_t));
|
||||||
rwlock->attr = 0;
|
|
||||||
} else {
|
if (__predict_false(attr != NULL)) {
|
||||||
switch (*attr) {
|
rwlock->pshared = __rwlockattr_getpshared(attr);
|
||||||
case PTHREAD_PROCESS_SHARED:
|
int kind = __rwlockattr_getkind(attr);
|
||||||
case PTHREAD_PROCESS_PRIVATE:
|
switch (kind) {
|
||||||
rwlock->attr= *attr;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
if ((*attr & RWLOCKATTR_RESERVED_MASK) != 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_init(&rwlock->state, 0);
|
atomic_init(&rwlock->state, 0);
|
||||||
atomic_init(&rwlock->writer_thread_id, 0);
|
rwlock->pending_lock.init(rwlock->pshared);
|
||||||
atomic_init(&rwlock->pending_readers, 0);
|
|
||||||
atomic_init(&rwlock->pending_writers, 0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,105 +260,173 @@ int pthread_rwlock_destroy(pthread_rwlock_t* rwlock_interface) {
|
|||||||
return 0;
|
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,
|
static int __pthread_rwlock_timedrdlock(pthread_rwlock_internal_t* rwlock,
|
||||||
const timespec* abs_timeout_or_null) {
|
const timespec* abs_timeout_or_null) {
|
||||||
|
|
||||||
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) {
|
||||||
memory_order_relaxed))) {
|
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
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);
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
if (__predict_true(old_state >= 0)) {
|
if (__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
|
||||||
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, old_state + 1,
|
continue;
|
||||||
memory_order_acquire, memory_order_relaxed)) {
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timespec ts;
|
|
||||||
timespec* rel_timeout = NULL;
|
|
||||||
|
|
||||||
if (abs_timeout_or_null != NULL) {
|
timespec ts;
|
||||||
rel_timeout = &ts;
|
timespec* rel_timeout = NULL;
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To avoid losing wake ups, the pending_readers increment should be observed before
|
if (abs_timeout_or_null != NULL) {
|
||||||
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
rel_timeout = &ts;
|
||||||
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||||
// 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) {
|
|
||||||
return ETIMEDOUT;
|
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,
|
static int __pthread_rwlock_timedwrlock(pthread_rwlock_internal_t* rwlock,
|
||||||
const timespec* abs_timeout_or_null) {
|
const timespec* abs_timeout_or_null) {
|
||||||
|
|
||||||
if (__predict_false(__get_thread()->tid == atomic_load_explicit(&rwlock->writer_thread_id,
|
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) == __get_thread()->tid) {
|
||||||
memory_order_relaxed))) {
|
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
int ret = __pthread_rwlock_trywrlock(rwlock);
|
||||||
|
if (ret == 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
if (__predict_true(old_state == 0)) {
|
if (__can_acquire_write_lock(old_state)) {
|
||||||
if (atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, -1,
|
continue;
|
||||||
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 (abs_timeout_or_null != NULL) {
|
timespec ts;
|
||||||
rel_timeout = &ts;
|
timespec* rel_timeout = NULL;
|
||||||
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To avoid losing wake ups, the pending_writers increment should be observed before
|
if (abs_timeout_or_null != NULL) {
|
||||||
// futex_wait by all threads. A seq_cst fence instead of a seq_cst operation is used
|
rel_timeout = &ts;
|
||||||
// here. Because only a seq_cst fence can ensure sequential consistency for non-atomic
|
if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
|
||||||
// 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) {
|
|
||||||
return ETIMEDOUT;
|
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) {
|
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(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);
|
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) {
|
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
return __pthread_rwlock_tryrdlock(__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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) {
|
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(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);
|
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) {
|
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
return __pthread_rwlock_trywrlock(__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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_interface) {
|
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_interface) {
|
||||||
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
pthread_rwlock_internal_t* rwlock = __get_internal_rwlock(rwlock_interface);
|
||||||
|
|
||||||
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
|
||||||
if (__predict_false(old_state == 0)) {
|
if (__state_owned_by_writer(old_state)) {
|
||||||
return EPERM;
|
if (atomic_load_explicit(&rwlock->writer_tid, memory_order_relaxed) != __get_thread()->tid) {
|
||||||
} else if (old_state == -1) {
|
|
||||||
if (atomic_load_explicit(&rwlock->writer_thread_id, memory_order_relaxed) != __get_thread()->tid) {
|
|
||||||
return EPERM;
|
return EPERM;
|
||||||
}
|
}
|
||||||
// We're no longer the owner.
|
atomic_store_explicit(&rwlock->writer_tid, 0, memory_order_relaxed);
|
||||||
atomic_store_explicit(&rwlock->writer_thread_id, 0, memory_order_relaxed);
|
old_state = atomic_fetch_and_explicit(&rwlock->state, ~STATE_OWNED_BY_WRITER_FLAG,
|
||||||
// Change state from -1 to 0.
|
memory_order_release);
|
||||||
atomic_store_explicit(&rwlock->state, 0, memory_order_release);
|
if (!__state_have_pending_readers_or_writers(old_state)) {
|
||||||
|
|
||||||
} 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) {
|
|
||||||
return 0;
|
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.
|
// Wake up pending readers or writers.
|
||||||
// To avoid losing wake ups, the update of state should be observed before reading
|
rwlock->pending_lock.lock();
|
||||||
// pending_readers/pending_writers by all threads. Use read locking as an example:
|
if (rwlock->pending_writer_count != 0) {
|
||||||
// read locking thread unlocking thread
|
rwlock->pending_writer_wakeup_serial++;
|
||||||
// pending_readers++; state = 0;
|
rwlock->pending_lock.unlock();
|
||||||
// seq_cst fence seq_cst fence
|
|
||||||
// read state for futex_wait read pending_readers for futex_wake
|
__futex_wake_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared, 1);
|
||||||
//
|
|
||||||
// So when locking and unlocking threads are running in parallel, we will not get
|
} else if (rwlock->pending_reader_count != 0) {
|
||||||
// in a situation that the locking thread reads state as negative and needs to wait,
|
rwlock->pending_reader_wakeup_serial++;
|
||||||
// while the unlocking thread reads pending_readers as zero and doesn't need to wake up waiters.
|
rwlock->pending_lock.unlock();
|
||||||
atomic_thread_fence(memory_order_seq_cst);
|
|
||||||
if (__predict_false(atomic_load_explicit(&rwlock->pending_readers, memory_order_relaxed) > 0 ||
|
__futex_wake_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared, INT_MAX);
|
||||||
atomic_load_explicit(&rwlock->pending_writers, memory_order_relaxed) > 0)) {
|
|
||||||
__futex_wake_ex(&rwlock->state, rwlock->process_shared(), INT_MAX);
|
} else {
|
||||||
|
// It happens when waiters are woken up by timeout.
|
||||||
|
rwlock->pending_lock.unlock();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#undef _FORTIFY_SOURCE
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.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;
|
appid = android_ids[n].aid;
|
||||||
// Move the end pointer to the null terminator.
|
// Move the end pointer to the null terminator.
|
||||||
end += strlen(android_ids[n].name) + 1;
|
end += strlen(android_ids[n].name) + 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -598,6 +598,16 @@ int __system_property_area_init()
|
|||||||
return map_prop_area_rw();
|
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)
|
const prop_info *__system_property_find(const char *name)
|
||||||
{
|
{
|
||||||
if (__predict_false(compat_mode)) {
|
if (__predict_false(compat_mode)) {
|
||||||
|
|||||||
@@ -48,6 +48,71 @@ typedef struct {
|
|||||||
} a_un;
|
} a_un;
|
||||||
} Elf64_auxv_t;
|
} 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_ORIGIN 0x00000001
|
||||||
#define DF_SYMBOLIC 0x00000002
|
#define DF_SYMBOLIC 0x00000002
|
||||||
#define DF_TEXTREL 0x00000004
|
#define DF_TEXTREL 0x00000004
|
||||||
@@ -129,4 +194,10 @@ typedef struct {
|
|||||||
|
|
||||||
#define NT_GNU_BUILD_ID 3
|
#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 */
|
#endif /* _ELF_H */
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ typedef long pthread_rwlockattr_t;
|
|||||||
|
|
||||||
#define PTHREAD_RWLOCK_INITIALIZER { { 0 } }
|
#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_key_t;
|
||||||
|
|
||||||
typedef int pthread_once_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_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_destroy(pthread_rwlockattr_t*) __nonnull((1));
|
||||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t*, int*) __nonnull((1, 2));
|
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_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_destroy(pthread_rwlock_t*) __nonnull((1));
|
||||||
int pthread_rwlock_init(pthread_rwlock_t*, const pthread_rwlockattr_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>
|
#include <android/legacy_stdlib_inlines.h>
|
||||||
#endif
|
#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
|
__END_DECLS
|
||||||
|
|
||||||
#endif /* _STDLIB_H */
|
#endif /* _STDLIB_H */
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ extern char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1));
|
|||||||
#define __bionic_using_gnu_basename
|
#define __bionic_using_gnu_basename
|
||||||
#endif
|
#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* __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 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);
|
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)
|
#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
|
__BIONIC_FORTIFY_INLINE
|
||||||
void* memcpy(void* __restrict dest, const void* __restrict src, size_t copy_amount) {
|
void* memcpy(void* __restrict dest, const void* __restrict src, size_t copy_amount) {
|
||||||
return __builtin___memcpy_chk(dest, src, copy_amount, __bos0(dest));
|
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_RAMDISK_DEFAULT "/default.prop"
|
||||||
#define PROP_PATH_SYSTEM_BUILD "/system/build.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_VENDOR_BUILD "/vendor/build.prop"
|
||||||
#define PROP_PATH_BOOTIMAGE_BUILD "/build.prop"
|
#define PROP_PATH_BOOTIMAGE_BUILD "/build.prop"
|
||||||
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.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();
|
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
|
/* Add a new system property. Can only be done by a single
|
||||||
** process that has write access to the property area, and
|
** process that has write access to the property area, and
|
||||||
** that process must handle sequencing to ensure the property
|
** 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); \
|
} while (_rc == -1 && errno == EINTR); \
|
||||||
_rc; })
|
_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);
|
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_dest_size_error, "read called with size bigger than destination");
|
||||||
__errordecl(__read_count_toobig_error, "read called with count > SSIZE_MAX");
|
__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 __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(__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
|
__BIONIC_FORTIFY_INLINE
|
||||||
ssize_t read(int fd, void* buf, size_t count) {
|
ssize_t read(int fd, void* buf, size_t count) {
|
||||||
size_t bos = __bos0(buf);
|
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);
|
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) */
|
#endif /* defined(__BIONIC_FORTIFY) */
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ if top is None:
|
|||||||
|
|
||||||
# Set up the env vars for libclang.
|
# Set up the env vars for libclang.
|
||||||
site.addsitedir(os.path.join(top, 'external/clang/bindings/python'))
|
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
|
import clang.cindex
|
||||||
from clang.cindex import conf
|
from clang.cindex import conf
|
||||||
@@ -26,6 +25,10 @@ from clang.cindex import TokenGroup
|
|||||||
from clang.cindex import TokenKind
|
from clang.cindex import TokenKind
|
||||||
from clang.cindex import TranslationUnit
|
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 kCppUndefinedMacro
|
||||||
from defaults import kernel_remove_config_macros
|
from defaults import kernel_remove_config_macros
|
||||||
from defaults import kernel_token_replacements
|
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
|
#define LIBC_PTHREAD_KEY_RESERVED_COUNT 12
|
||||||
|
|
||||||
#if defined(USE_JEMALLOC)
|
#if defined(USE_JEMALLOC)
|
||||||
/* Following are current pthread keys used internally by jemalloc:
|
/* Internally, jemalloc uses a single key for per thread data. */
|
||||||
* je_thread_allocated_tsd jemalloc
|
#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 1
|
||||||
* je_arenas_tsd jemalloc
|
|
||||||
* je_tcache_tsd jemalloc
|
|
||||||
* je_tcache_enabled_tsd jemalloc
|
|
||||||
* je_quarantine_tsd jemalloc
|
|
||||||
*/
|
|
||||||
#define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 5
|
|
||||||
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT)
|
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT (LIBC_PTHREAD_KEY_RESERVED_COUNT + JEMALLOC_PTHREAD_KEY_RESERVED_COUNT)
|
||||||
#else
|
#else
|
||||||
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT
|
#define BIONIC_PTHREAD_KEY_RESERVED_COUNT LIBC_PTHREAD_KEY_RESERVED_COUNT
|
||||||
|
|||||||
@@ -35,11 +35,15 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "atexit.h"
|
#include "atexit.h"
|
||||||
#include "thread_private.h"
|
#include "private/thread_private.h"
|
||||||
|
|
||||||
struct atexit *__atexit;
|
struct atexit *__atexit;
|
||||||
static int restartloop;
|
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
|
* Function pointers are stored in a linked list of pages. The list
|
||||||
* is initially empty, and pages are allocated on demand. The first
|
* 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 *p = __atexit;
|
||||||
struct atexit_fn *fnp;
|
struct atexit_fn *fnp;
|
||||||
int pgsize = getpagesize();
|
size_t pgsize = getpagesize();
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (pgsize < sizeof(*p))
|
if (pgsize < sizeof(*p))
|
||||||
@@ -161,6 +165,12 @@ restart:
|
|||||||
__atexit = NULL;
|
__atexit = NULL;
|
||||||
}
|
}
|
||||||
_ATEXIT_UNLOCK();
|
_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))
|
__atexit_register_cleanup(void (*func)(void))
|
||||||
{
|
{
|
||||||
struct atexit *p;
|
struct atexit *p;
|
||||||
int pgsize = getpagesize();
|
size_t pgsize = getpagesize();
|
||||||
|
|
||||||
if (pgsize < sizeof(*p))
|
if (pgsize < sizeof(*p))
|
||||||
return;
|
return;
|
||||||
@@ -32,8 +32,6 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "atexit.h"
|
|
||||||
#include "thread_private.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This variable is zero until a process has created a thread.
|
* This variable is zero until a process has created a thread.
|
||||||
@@ -44,12 +42,21 @@
|
|||||||
*/
|
*/
|
||||||
int __isthreaded = 0;
|
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.
|
* Exit, flushing stdio buffers if necessary.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
exit(int status)
|
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()
|
* Call functions registered by atexit() or _cxa_atexit()
|
||||||
* (including the stdio cleanup routine) and then _exit().
|
* (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')
|
CheckDirExists(bionic_libc_tools_zoneinfo_dir, 'bionic/libc/tools/zoneinfo')
|
||||||
print 'Found bionic in %s ...' % bionic_dir
|
print 'Found bionic in %s ...' % bionic_dir
|
||||||
|
|
||||||
# Find the icu4c directory.
|
# Find the icu directory.
|
||||||
icu_dir = os.path.realpath('%s/../external/icu/icu4c/source' % bionic_dir)
|
icu_dir = os.path.realpath('%s/../external/icu' % bionic_dir)
|
||||||
CheckDirExists(icu_dir, 'external/icu/icu4c/source')
|
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
|
print 'Found icu in %s ...' % icu_dir
|
||||||
|
|
||||||
|
|
||||||
@@ -116,14 +119,14 @@ def BuildIcuToolsAndData(data_filename):
|
|||||||
|
|
||||||
# Build the ICU tools.
|
# Build the ICU tools.
|
||||||
print 'Configuring 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.
|
# Run the ICU tools.
|
||||||
os.chdir('tools/tzcode')
|
os.chdir('tools/tzcode')
|
||||||
|
|
||||||
# The tz2icu tool only picks up icuregions and icuzones in they are in the CWD
|
# The tz2icu tool only picks up icuregions and icuzones in they are in the CWD
|
||||||
for icu_data_file in [ 'icuregions', 'icuzones']:
|
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
|
icu_data_file_symlink = './%s' % icu_data_file
|
||||||
os.symlink(icu_data_file_source, icu_data_file_symlink)
|
os.symlink(icu_data_file_source, icu_data_file_symlink)
|
||||||
|
|
||||||
@@ -134,7 +137,7 @@ def BuildIcuToolsAndData(data_filename):
|
|||||||
subprocess.check_call(['make'])
|
subprocess.check_call(['make'])
|
||||||
|
|
||||||
# Copy the source file to its ultimate destination.
|
# 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
|
print 'Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir
|
||||||
shutil.copy('zoneinfo64.txt', 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'])
|
subprocess.check_call(['make', 'INCLUDE_UNI_CORE_DATA=1', '-j32'])
|
||||||
|
|
||||||
# Copy the .dat file to its ultimate destination.
|
# 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')
|
datfiles = glob.glob('data/out/tmp/icudt??l.dat')
|
||||||
if len(datfiles) != 1:
|
if len(datfiles) != 1:
|
||||||
print 'ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles)
|
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)
|
print 'Copying %s to %s ...' % (datfile, icu_dat_data_dir)
|
||||||
shutil.copy(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.
|
# Switch back to the original working cwd.
|
||||||
os.chdir(original_working_dir)
|
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;
|
soinfo* found = nullptr;
|
||||||
ElfW(Sym)* sym = nullptr;
|
const ElfW(Sym)* sym = nullptr;
|
||||||
void* caller_addr = __builtin_return_address(0);
|
void* caller_addr = __builtin_return_address(0);
|
||||||
soinfo* caller = find_containing_library(caller_addr);
|
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 copy_to_array(T* array[], size_t array_length) const {
|
||||||
size_t sz = 0;
|
size_t sz = 0;
|
||||||
for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -55,27 +55,12 @@
|
|||||||
#include "linker_block_allocator.h"
|
#include "linker_block_allocator.h"
|
||||||
#include "linker_debug.h"
|
#include "linker_debug.h"
|
||||||
#include "linker_environ.h"
|
#include "linker_environ.h"
|
||||||
#include "linker_leb128.h"
|
#include "linker_sleb128.h"
|
||||||
#include "linker_phdr.h"
|
#include "linker_phdr.h"
|
||||||
#include "linker_relocs.h"
|
#include "linker_relocs.h"
|
||||||
#include "linker_reloc_iterators.h"
|
#include "linker_reloc_iterators.h"
|
||||||
#include "ziparchive/zip_archive.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
|
// Override macros to use C++ style casts
|
||||||
#undef ELF_ST_TYPE
|
#undef ELF_ST_TYPE
|
||||||
#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
|
#define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
|
||||||
@@ -100,6 +85,9 @@ static const char* const kDefaultLdPaths[] = {
|
|||||||
nullptr
|
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_library_paths;
|
||||||
static std::vector<std::string> g_ld_preload_names;
|
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;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
|
const ElfW(Versym)* soinfo::get_versym(size_t n) const {
|
||||||
return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
|
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) {
|
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;
|
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 hash = symbol_name.gnu_hash();
|
||||||
uint32_t h2 = hash >> gnu_shift2_;
|
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_;
|
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
|
||||||
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
|
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
|
||||||
|
|
||||||
|
*symbol_index = 0;
|
||||||
|
|
||||||
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
|
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
|
||||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
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",
|
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||||
|
|
||||||
return nullptr;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bloom test says "probably yes"...
|
// 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",
|
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
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 {
|
do {
|
||||||
ElfW(Sym)* s = symtab_ + n;
|
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 &&
|
if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
|
||||||
|
check_symbol_version(verneed, verdef) &&
|
||||||
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
||||||
is_symbol_global_and_defined(this, s)) {
|
is_symbol_global_and_defined(this, s)) {
|
||||||
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
||||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(s->st_value),
|
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(s->st_value),
|
||||||
static_cast<size_t>(s->st_size));
|
static_cast<size_t>(s->st_size));
|
||||||
return s;
|
*symbol_index = n;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} while ((gnu_chain_[n++] & 1) == 0);
|
} while ((gnu_chain_[n++] & 1) == 0);
|
||||||
|
|
||||||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
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();
|
uint32_t hash = symbol_name.elf_hash();
|
||||||
|
|
||||||
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
|
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
|
||||||
symbol_name.get_name(), get_soname(),
|
symbol_name.get_name(), get_soname(),
|
||||||
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
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]) {
|
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
|
||||||
ElfW(Sym)* s = symtab_ + 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)) {
|
is_symbol_global_and_defined(this, s)) {
|
||||||
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
|
||||||
symbol_name.get_name(), get_soname(),
|
symbol_name.get_name(), get_soname(),
|
||||||
reinterpret_cast<void*>(s->st_value),
|
reinterpret_cast<void*>(s->st_value),
|
||||||
static_cast<size_t>(s->st_size));
|
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(),
|
symbol_name.get_name(), get_soname(),
|
||||||
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
||||||
|
|
||||||
return nullptr;
|
*symbol_index = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
|
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
|
||||||
@@ -523,10 +685,11 @@ uint32_t SymbolName::gnu_hash() {
|
|||||||
return gnu_hash_;
|
return gnu_hash_;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
|
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);
|
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
|
/* "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
|
* 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) {
|
if (si_from->has_DT_SYMBOLIC) {
|
||||||
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
|
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
|
||||||
s = si_from->find_symbol_by_name(symbol_name);
|
if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (s != nullptr) {
|
if (s != nullptr) {
|
||||||
*si_found_in = si_from;
|
*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
|
// 1. Look for it in global_group
|
||||||
if (s == nullptr) {
|
if (s == nullptr) {
|
||||||
|
bool error = false;
|
||||||
global_group.visit([&](soinfo* global_si) {
|
global_group.visit([&](soinfo* global_si) {
|
||||||
DEBUG("%s: looking up %s in %s (from global group)",
|
DEBUG("%s: looking up %s in %s (from global group)",
|
||||||
si_from->get_soname(), name, global_si->get_soname());
|
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) {
|
if (s != nullptr) {
|
||||||
*si_found_in = global_si;
|
*si_found_in = global_si;
|
||||||
return false;
|
return false;
|
||||||
@@ -560,10 +731,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Look for it in the local group
|
// 2. Look for it in the local group
|
||||||
if (s == nullptr) {
|
if (s == nullptr) {
|
||||||
|
bool error = false;
|
||||||
local_group.visit([&](soinfo* local_si) {
|
local_group.visit([&](soinfo* local_si) {
|
||||||
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
|
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
|
||||||
// we already did this - skip
|
// 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)",
|
DEBUG("%s: looking up %s in %s (from local group)",
|
||||||
si_from->get_soname(), name, local_si->get_soname());
|
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) {
|
if (s != nullptr) {
|
||||||
*si_found_in = local_si;
|
*si_found_in = local_si;
|
||||||
return false;
|
return false;
|
||||||
@@ -580,6 +760,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s != nullptr) {
|
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));
|
reinterpret_cast<void*>((*si_found_in)->load_bias));
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
*symbol = s;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProtectedDataGuard {
|
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
|
// This is used by dlsym(3). It performs symbol lookup only within the
|
||||||
// specified soinfo object and its dependencies in breadth first order.
|
// specified soinfo object and its dependencies in breadth first order.
|
||||||
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) {
|
||||||
ElfW(Sym)* result = nullptr;
|
const ElfW(Sym)* result = nullptr;
|
||||||
SymbolName symbol_name(name);
|
SymbolName symbol_name(name);
|
||||||
|
|
||||||
|
|
||||||
walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
|
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) {
|
if (result != nullptr) {
|
||||||
*found = current_soinfo;
|
*found = current_soinfo;
|
||||||
return false;
|
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
|
beginning of the global solist. Otherwise the search starts at the
|
||||||
specified soinfo (for RTLD_NEXT).
|
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);
|
SymbolName symbol_name(name);
|
||||||
|
|
||||||
soinfo* start = solist;
|
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) {
|
for (soinfo* si = start; si != nullptr; si = si->next) {
|
||||||
if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
|
if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = si->find_symbol_by_name(symbol_name);
|
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (s != nullptr) {
|
if (s != nullptr) {
|
||||||
*found = si;
|
*found = si;
|
||||||
break;
|
break;
|
||||||
@@ -800,7 +994,10 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = si->find_symbol_by_name(symbol_name);
|
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (s != nullptr) {
|
if (s != nullptr) {
|
||||||
*found = si;
|
*found = si;
|
||||||
break;
|
break;
|
||||||
@@ -1444,6 +1641,93 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
|
|||||||
return ifunc_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(__mips__)
|
||||||
#if defined(USE_RELA)
|
#if defined(USE_RELA)
|
||||||
static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
|
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>
|
template<typename ElfRelIteratorT>
|
||||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
|
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
|
||||||
const soinfo_list_t& local_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) {
|
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||||
const auto rel = rel_iterator.next();
|
const auto rel = rel_iterator.next();
|
||||||
if (rel == nullptr) {
|
if (rel == nullptr) {
|
||||||
@@ -1481,12 +1771,32 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfW(Sym)* s = nullptr;
|
const ElfW(Sym)* s = nullptr;
|
||||||
soinfo* lsi = nullptr;
|
soinfo* lsi = nullptr;
|
||||||
|
|
||||||
if (sym != 0) {
|
if (sym != 0) {
|
||||||
sym_name = get_string(symtab_[sym].st_name);
|
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) {
|
if (s == nullptr) {
|
||||||
// We only allow an undefined symbol if this is a weak reference...
|
// We only allow an undefined symbol if this is a weak reference...
|
||||||
s = &symtab_[sym];
|
s = &symtab_[sym];
|
||||||
@@ -1977,6 +2287,14 @@ soinfo::soinfo_list_t& soinfo::get_children() {
|
|||||||
return g_empty_list;
|
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() {
|
soinfo::soinfo_list_t& soinfo::get_parents() {
|
||||||
if (has_min_version(0)) {
|
if (has_min_version(0)) {
|
||||||
return parents_;
|
return parents_;
|
||||||
@@ -1985,7 +2303,7 @@ soinfo::soinfo_list_t& soinfo::get_parents() {
|
|||||||
return g_empty_list;
|
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) {
|
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
|
||||||
return call_ifunc_resolver(s->st_value + load_bias);
|
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 */
|
/* We can't log anything until the linker is relocated */
|
||||||
bool relocating_linker = (flags_ & FLAG_LINKER) != 0;
|
bool relocating_linker = (flags_ & FLAG_LINKER) != 0;
|
||||||
if (!relocating_linker) {
|
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_);
|
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:
|
case DT_BIND_NOW:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Ignore: bionic does not support symbol versioning...
|
|
||||||
case DT_VERSYM:
|
case DT_VERSYM:
|
||||||
|
versym_ = reinterpret_cast<ElfW(Versym)*>(load_bias + d->d_un.d_ptr);
|
||||||
|
break;
|
||||||
|
|
||||||
case DT_VERDEF:
|
case DT_VERDEF:
|
||||||
|
verdef_ptr_ = load_bias + d->d_un.d_ptr;
|
||||||
|
break;
|
||||||
case DT_VERDEFNUM:
|
case DT_VERDEFNUM:
|
||||||
|
verdef_cnt_ = d->d_un.d_val;
|
||||||
|
break;
|
||||||
|
|
||||||
case DT_VERNEED:
|
case DT_VERNEED:
|
||||||
|
verneed_ptr_ = load_bias + d->d_un.d_ptr;
|
||||||
|
break;
|
||||||
|
|
||||||
case DT_VERNEEDNUM:
|
case DT_VERNEEDNUM:
|
||||||
|
verneed_cnt_ = d->d_un.d_val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2531,7 +2860,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
|
|||||||
if (android_relocs_size_ > 3 &&
|
if (android_relocs_size_ > 3 &&
|
||||||
android_relocs_[0] == 'A' &&
|
android_relocs_[0] == 'A' &&
|
||||||
android_relocs_[1] == 'P' &&
|
android_relocs_[1] == 'P' &&
|
||||||
(android_relocs_[2] == 'U' || android_relocs_[2] == 'S') &&
|
android_relocs_[2] == 'S' &&
|
||||||
android_relocs_[3] == '2') {
|
android_relocs_[3] == '2') {
|
||||||
DEBUG("[ android relocating %s ]", get_soname());
|
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 uint8_t* packed_relocs = android_relocs_ + 4;
|
||||||
const size_t packed_relocs_size = android_relocs_size_ - 4;
|
const size_t packed_relocs_size = android_relocs_size_ - 4;
|
||||||
|
|
||||||
if (android_relocs_[2] == 'U') {
|
relocated = relocate(
|
||||||
relocated = relocate(
|
packed_reloc_iterator<sleb128_decoder>(
|
||||||
packed_reloc_iterator<leb128_decoder>(
|
sleb128_decoder(packed_relocs, packed_relocs_size)),
|
||||||
leb128_decoder(packed_relocs, packed_relocs_size)),
|
global_group, local_group);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relocated) {
|
if (!relocated) {
|
||||||
return false;
|
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) {
|
for (const auto& ld_preload_name : g_ld_preload_names) {
|
||||||
needed_library_name_list.push_back(ld_preload_name.c_str());
|
needed_library_name_list.push_back(ld_preload_name.c_str());
|
||||||
++needed_libraries_count;
|
++needed_libraries_count;
|
||||||
|
++ld_preloads_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each_dt_needed(si, [&](const char* name) {
|
for_each_dt_needed(si, [&](const char* name) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include "linked_list.h"
|
#include "linked_list.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define DL_ERR(fmt, x...) \
|
#define DL_ERR(fmt, x...) \
|
||||||
do { \
|
do { \
|
||||||
@@ -142,6 +143,32 @@ class SymbolName {
|
|||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(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 {
|
struct soinfo {
|
||||||
public:
|
public:
|
||||||
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
||||||
@@ -260,11 +287,16 @@ struct soinfo {
|
|||||||
void set_dt_flags_1(uint32_t dt_flags_1);
|
void set_dt_flags_1(uint32_t dt_flags_1);
|
||||||
|
|
||||||
soinfo_list_t& get_children();
|
soinfo_list_t& get_children();
|
||||||
|
const soinfo_list_t& get_children() const;
|
||||||
|
|
||||||
soinfo_list_t& get_parents();
|
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(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;
|
const char* get_string(ElfW(Word) index) const;
|
||||||
bool can_unload() const;
|
bool can_unload() const;
|
||||||
@@ -292,11 +324,18 @@ struct soinfo {
|
|||||||
|
|
||||||
const char* get_soname() const;
|
const char* get_soname() const;
|
||||||
const char* get_realpath() 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:
|
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)* 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);
|
ElfW(Sym)* gnu_addr_lookup(const void* addr);
|
||||||
|
|
||||||
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
|
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_;
|
const char* soname_;
|
||||||
std::string realpath_;
|
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();
|
friend soinfo* get_libdl_info();
|
||||||
};
|
};
|
||||||
|
|
||||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
|
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
|
||||||
|
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol);
|
||||||
|
|
||||||
enum RelocationKind {
|
enum RelocationKind {
|
||||||
kRelocAbsolute = 0,
|
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);
|
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo);
|
||||||
void do_dlclose(soinfo* si);
|
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);
|
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();
|
void debuggerd_init();
|
||||||
extern "C" abort_msg_t* g_abort_message;
|
extern "C" abort_msg_t* g_abort_message;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
|
|
||||||
#include "private/KernelArgumentBlock.h"
|
#include "private/KernelArgumentBlock.h"
|
||||||
|
|
||||||
static char** _envp;
|
|
||||||
static bool _AT_SECURE_value = true;
|
static bool _AT_SECURE_value = true;
|
||||||
|
|
||||||
bool get_AT_SECURE() {
|
bool get_AT_SECURE() {
|
||||||
@@ -150,8 +149,8 @@ static bool __is_unsafe_environment_variable(const char* name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void __sanitize_environment_variables() {
|
static void __sanitize_environment_variables() {
|
||||||
char** src = _envp;
|
char** src = environ;
|
||||||
char** dst = _envp;
|
char** dst = environ;
|
||||||
for (; src[0] != nullptr; ++src) {
|
for (; src[0] != nullptr; ++src) {
|
||||||
if (!__is_valid_environment_variable(src[0])) {
|
if (!__is_valid_environment_variable(src[0])) {
|
||||||
continue;
|
continue;
|
||||||
@@ -168,7 +167,7 @@ static void __sanitize_environment_variables() {
|
|||||||
|
|
||||||
void linker_env_init(KernelArgumentBlock& args) {
|
void linker_env_init(KernelArgumentBlock& args) {
|
||||||
// Store environment pointer - can't be null.
|
// Store environment pointer - can't be null.
|
||||||
_envp = args.envp;
|
environ = args.envp;
|
||||||
|
|
||||||
__init_AT_SECURE(args);
|
__init_AT_SECURE(args);
|
||||||
__sanitize_environment_variables();
|
__sanitize_environment_variables();
|
||||||
@@ -179,7 +178,7 @@ const char* linker_env_get(const char* name) {
|
|||||||
return nullptr;
|
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);
|
const char* val = env_match(p[0], name);
|
||||||
if (val != nullptr) {
|
if (val != nullptr) {
|
||||||
if (val[0] == '\0') {
|
if (val[0] == '\0') {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include "linker_debug.h"
|
#include "linker_debug.h"
|
||||||
#include "linker_relocs.h"
|
#include "linker_relocs.h"
|
||||||
#include "linker_reloc_iterators.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,
|
template bool soinfo::relocate<plain_reloc_iterator>(plain_reloc_iterator&& rel_iterator,
|
||||||
const soinfo_list_t& global_group,
|
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& global_group,
|
||||||
const soinfo_list_t& local_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>
|
template <typename ElfRelIteratorT>
|
||||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
||||||
const soinfo_list_t& global_group,
|
const soinfo_list_t& global_group,
|
||||||
const soinfo_list_t& local_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) {
|
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||||
const auto rel = rel_iterator.next();
|
const auto rel = rel_iterator.next();
|
||||||
|
|
||||||
@@ -69,12 +70,33 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfW(Sym)* s = nullptr;
|
const ElfW(Sym)* s = nullptr;
|
||||||
soinfo* lsi = nullptr;
|
soinfo* lsi = nullptr;
|
||||||
|
|
||||||
if (sym != 0) {
|
if (sym != 0) {
|
||||||
sym_name = get_string(symtab_[sym].st_name);
|
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) {
|
if (s == nullptr) {
|
||||||
// mips does not support relocation with weak-undefined symbols
|
// mips does not support relocation with weak-undefined symbols
|
||||||
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname());
|
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.
|
// This is an undefined reference... try to locate it.
|
||||||
const char* sym_name = get_string(sym->st_name);
|
const char* sym_name = get_string(sym->st_name);
|
||||||
soinfo* lsi = nullptr;
|
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) {
|
if (s == nullptr) {
|
||||||
// We only allow an undefined symbol if this is a weak reference.
|
// We only allow an undefined symbol if this is a weak reference.
|
||||||
s = &symtab_[g];
|
s = &symtab_[g];
|
||||||
|
|||||||
@@ -14,42 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINKER_LEB128_H
|
#ifndef _LINKER_SLEB128_H
|
||||||
#define _LINKER_LEB128_H
|
#define _LINKER_SLEB128_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// Helper classes for decoding LEB128, used in packed relocation data.
|
// Helper classes for decoding LEB128, used in packed relocation data.
|
||||||
// http://en.wikipedia.org/wiki/LEB128
|
// 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 {
|
class sleb128_decoder {
|
||||||
public:
|
public:
|
||||||
sleb128_decoder(const uint8_t* buffer, size_t count)
|
sleb128_decoder(const uint8_t* buffer, size_t count)
|
||||||
@@ -64,7 +36,7 @@ class sleb128_decoder {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if (current_ >= end_) {
|
if (current_ >= end_) {
|
||||||
__libc_fatal("leb128_decoder ran out of bounds");
|
__libc_fatal("sleb128_decoder ran out of bounds");
|
||||||
}
|
}
|
||||||
byte = *current_++;
|
byte = *current_++;
|
||||||
value |= (static_cast<size_t>(byte & 127) << shift);
|
value |= (static_cast<size_t>(byte & 127) << shift);
|
||||||
@@ -83,5 +55,4 @@ class sleb128_decoder {
|
|||||||
const uint8_t* const end_;
|
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.
|
# which bionic does not support. Reenable this once this question is resolved.
|
||||||
bionic-unit-tests_clang_target := false
|
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
|
bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global
|
||||||
endif
|
|
||||||
|
|
||||||
module := bionic-unit-tests
|
module := bionic-unit-tests
|
||||||
module_tag := optional
|
module_tag := optional
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ClassWithDtor {
|
|||||||
std::string message;
|
std::string message;
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local ClassWithDtor class_with_dtor;
|
static thread_local ClassWithDtor class_with_dtor;
|
||||||
|
|
||||||
static void* thread_nop(void* arg) {
|
static void* thread_nop(void* arg) {
|
||||||
class_with_dtor.set_message(*static_cast<std::string*>(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);
|
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);
|
extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle);
|
||||||
|
|
||||||
static void thread_atexit_fn1(void* arg) {
|
static void thread_atexit_fn1(void* arg) {
|
||||||
|
|||||||
@@ -134,6 +134,10 @@ TEST_F(DlExtTest, ExtInfoUseFdWithOffset) {
|
|||||||
|
|
||||||
TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
|
TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
|
||||||
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;
|
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;
|
android_dlextinfo extinfo;
|
||||||
extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
|
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;
|
extinfo.library_fd_offset = PAGE_SIZE;
|
||||||
handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo);
|
handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo);
|
||||||
ASSERT_TRUE(handle_ == nullptr);
|
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);
|
close(extinfo.library_fd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,7 +626,6 @@ TEST(dlfcn, dlopen_nodelete_dt_flags_1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(dlfcn, dlsym_df_1_global) {
|
TEST(dlfcn, dlsym_df_1_global) {
|
||||||
#if !defined(__arm__) && !defined(__aarch64__)
|
|
||||||
void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW);
|
void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW);
|
||||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||||
int (*get_answer)();
|
int (*get_answer)();
|
||||||
@@ -634,9 +633,6 @@ TEST(dlfcn, dlsym_df_1_global) {
|
|||||||
ASSERT_TRUE(get_answer != nullptr) << dlerror();
|
ASSERT_TRUE(get_answer != nullptr) << dlerror();
|
||||||
ASSERT_EQ(42, get_answer());
|
ASSERT_EQ(42, get_answer());
|
||||||
ASSERT_EQ(0, dlclose(handle));
|
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) {
|
TEST(dlfcn, dlopen_failure) {
|
||||||
@@ -764,7 +760,11 @@ TEST(dlfcn, dladdr_libc) {
|
|||||||
void* addr = reinterpret_cast<void*>(puts); // well-known libc function
|
void* addr = reinterpret_cast<void*>(puts); // well-known libc function
|
||||||
ASSERT_TRUE(dladdr(addr, &info) != 0);
|
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
|
// TODO: add check for dfi_fbase
|
||||||
ASSERT_STREQ("puts", info.dli_sname);
|
ASSERT_STREQ("puts", info.dli_sname);
|
||||||
ASSERT_EQ(addr, info.dli_saddr);
|
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";
|
GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
|
||||||
#endif
|
#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));
|
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) {
|
TEST_F(DEATHTEST, read_fortified) {
|
||||||
char buf[1];
|
char buf[1];
|
||||||
size_t ct = atoi("2"); // prevent optimizations
|
size_t ct = atoi("2"); // prevent optimizations
|
||||||
@@ -631,6 +647,18 @@ TEST_F(DEATHTEST, read_fortified) {
|
|||||||
close(fd);
|
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* __strncat_chk(char*, const char*, size_t, size_t);
|
||||||
extern "C" char* __strcat_chk(char*, const char*, 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_dlsym.mk \
|
||||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.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.dlopen_check_order_reloc_main_executable.mk \
|
||||||
|
$(LOCAL_PATH)/Android.build.pthread_atfork.mk \
|
||||||
$(LOCAL_PATH)/Android.build.testlib.mk \
|
$(LOCAL_PATH)/Android.build.testlib.mk \
|
||||||
|
$(LOCAL_PATH)/Android.build.versioned_lib.mk \
|
||||||
$(TEST_PATH)/Android.build.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
|
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
|
# Library with dependency loop used by dlfcn tests
|
||||||
#
|
#
|
||||||
@@ -342,17 +354,17 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
|||||||
# Library with DF_1_GLOBAL
|
# Library with DF_1_GLOBAL
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
libdl_test_df_1_global_src_files := dl_df_1_global.cpp
|
libdl_test_df_1_global_src_files := dl_df_1_global.cpp
|
||||||
libdl_test_df_1_global_ldflags := -fuse-ld=bfd -Wl,-z,global
|
libdl_test_df_1_global_ldflags := -Wl,-z,global
|
||||||
module := libdl_test_df_1_global
|
# TODO (dimitry): x86* toolchain does not support -z global - switch to bfd
|
||||||
# TODO: re-enable arm once b/18137520 or b/18130452 are fixed
|
ifeq ($(filter $(TARGET_ARCH),x86 x86_64),$(TARGET_ARCH))
|
||||||
ifeq ($(filter $(TARGET_ARCH),arm arm64),)
|
libdl_test_df_1_global_ldflags_target := -fuse-ld=bfd
|
||||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
|
||||||
else
|
|
||||||
# build it for host only
|
|
||||||
build_target := SHARED_LIBRARY
|
|
||||||
build_type := host
|
|
||||||
include $(TEST_PATH)/Android.build.mk
|
|
||||||
endif
|
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
|
# 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 <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@@ -181,6 +182,19 @@ TEST(pthread, pthread_key_dirty) {
|
|||||||
ASSERT_EQ(0, pthread_key_delete(key));
|
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) {
|
static void* IdFn(void* arg) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
@@ -390,7 +404,9 @@ TEST(pthread, pthread_sigmask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(pthread, pthread_setname_np__too_long) {
|
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) {
|
TEST(pthread, pthread_setname_np__self) {
|
||||||
@@ -660,6 +676,37 @@ TEST(pthread, pthread_attr_setstacksize) {
|
|||||||
#endif // __BIONIC__
|
#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) {
|
TEST(pthread, pthread_rwlock_smoke) {
|
||||||
pthread_rwlock_t l;
|
pthread_rwlock_t l;
|
||||||
ASSERT_EQ(0, pthread_rwlock_init(&l, NULL));
|
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_wrlock(&l));
|
||||||
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
||||||
|
|
||||||
#ifdef __BIONIC__
|
|
||||||
// EDEADLK in "read after write"
|
// EDEADLK in "read after write"
|
||||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&l));
|
ASSERT_EQ(0, pthread_rwlock_wrlock(&l));
|
||||||
ASSERT_EQ(EDEADLK, pthread_rwlock_rdlock(&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(0, pthread_rwlock_wrlock(&l));
|
||||||
ASSERT_EQ(EDEADLK, pthread_rwlock_wrlock(&l));
|
ASSERT_EQ(EDEADLK, pthread_rwlock_wrlock(&l));
|
||||||
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
ASSERT_EQ(0, pthread_rwlock_unlock(&l));
|
||||||
#endif
|
|
||||||
|
|
||||||
ASSERT_EQ(0, pthread_rwlock_destroy(&l));
|
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));
|
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 int g_once_fn_call_count = 0;
|
||||||
static void OnceFn() {
|
static void OnceFn() {
|
||||||
++g_once_fn_call_count;
|
++g_once_fn_call_count;
|
||||||
@@ -840,14 +990,14 @@ TEST(pthread, pthread_once_1934122) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int g_atfork_prepare_calls = 0;
|
static int g_atfork_prepare_calls = 0;
|
||||||
static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 1; }
|
static void AtForkPrepare1() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 1; }
|
||||||
static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls << 4) | 2; }
|
static void AtForkPrepare2() { g_atfork_prepare_calls = (g_atfork_prepare_calls * 10) + 2; }
|
||||||
static int g_atfork_parent_calls = 0;
|
static int g_atfork_parent_calls = 0;
|
||||||
static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 1; }
|
static void AtForkParent1() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 1; }
|
||||||
static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls << 4) | 2; }
|
static void AtForkParent2() { g_atfork_parent_calls = (g_atfork_parent_calls * 10) + 2; }
|
||||||
static int g_atfork_child_calls = 0;
|
static int g_atfork_child_calls = 0;
|
||||||
static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 1; }
|
static void AtForkChild1() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 1; }
|
||||||
static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls << 4) | 2; }
|
static void AtForkChild2() { g_atfork_child_calls = (g_atfork_child_calls * 10) + 2; }
|
||||||
|
|
||||||
TEST(pthread, pthread_atfork_smoke) {
|
TEST(pthread, pthread_atfork_smoke) {
|
||||||
ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1));
|
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.
|
// Child and parent calls are made in the order they were registered.
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
ASSERT_EQ(0x12, g_atfork_child_calls);
|
ASSERT_EQ(12, g_atfork_child_calls);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
ASSERT_EQ(0x12, g_atfork_parent_calls);
|
ASSERT_EQ(12, g_atfork_parent_calls);
|
||||||
|
|
||||||
// Prepare calls are made in the reverse order.
|
// 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) {
|
TEST(pthread, pthread_attr_getscope) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "ScopedSignalHandler.h"
|
#include "ScopedSignalHandler.h"
|
||||||
|
|
||||||
@@ -197,7 +198,7 @@ TEST(time, timer_create) {
|
|||||||
ASSERT_EQ(0, timer_delete(timer_id));
|
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) {
|
static void timer_create_SIGEV_SIGNAL_signal_handler(int signal_number) {
|
||||||
++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
|
++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
|
||||||
ASSERT_EQ(SIGUSR1, signal_number);
|
ASSERT_EQ(SIGUSR1, signal_number);
|
||||||
@@ -212,6 +213,7 @@ TEST(time, timer_create_SIGEV_SIGNAL) {
|
|||||||
timer_t timer_id;
|
timer_t timer_id;
|
||||||
ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &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);
|
ScopedSignalHandler ssh(SIGUSR1, timer_create_SIGEV_SIGNAL_signal_handler);
|
||||||
|
|
||||||
ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
|
ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
|
||||||
@@ -228,25 +230,26 @@ TEST(time, timer_create_SIGEV_SIGNAL) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Counter {
|
struct Counter {
|
||||||
volatile int value;
|
private:
|
||||||
|
std::atomic<int> value;
|
||||||
timer_t timer_id;
|
timer_t timer_id;
|
||||||
sigevent_t se;
|
sigevent_t se;
|
||||||
bool timer_valid;
|
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() {
|
void Create() {
|
||||||
ASSERT_FALSE(timer_valid);
|
ASSERT_FALSE(timer_valid);
|
||||||
ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id));
|
ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id));
|
||||||
timer_valid = true;
|
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() {
|
void DeleteTimer() {
|
||||||
ASSERT_TRUE(timer_valid);
|
ASSERT_TRUE(timer_valid);
|
||||||
ASSERT_EQ(0, timer_delete(timer_id));
|
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) {
|
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);
|
::SetTime(timer_id, value_s, value_ns, interval_s, interval_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValueUpdated() {
|
bool ValueUpdated() {
|
||||||
volatile int current_value = value;
|
int current_value = value;
|
||||||
time_t start = time(NULL);
|
time_t start = time(NULL);
|
||||||
while (current_value == value && (time(NULL) - start) < 5) {
|
while (current_value == value && (time(NULL) - start) < 5) {
|
||||||
}
|
}
|
||||||
@@ -287,30 +294,29 @@ struct Counter {
|
|||||||
|
|
||||||
TEST(time, timer_settime_0) {
|
TEST(time, timer_settime_0) {
|
||||||
Counter counter(Counter::CountAndDisarmNotifyFunction);
|
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);
|
counter.SetTime(0, 1, 1, 0);
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
|
|
||||||
// The count should just be 1 because we disarmed the timer the first time it fired.
|
// 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) {
|
TEST(time, timer_settime_repeats) {
|
||||||
Counter counter(Counter::CountNotifyFunction);
|
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);
|
counter.SetTime(0, 1, 0, 10);
|
||||||
ASSERT_TRUE(counter.ValueUpdated());
|
ASSERT_TRUE(counter.ValueUpdated());
|
||||||
ASSERT_TRUE(counter.ValueUpdated());
|
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) {
|
static void timer_create_NULL_signal_handler(int signal_number) {
|
||||||
++timer_create_NULL_signal_handler_invocation_count;
|
++timer_create_NULL_signal_handler_invocation_count;
|
||||||
ASSERT_EQ(SIGALRM, signal_number);
|
ASSERT_EQ(SIGALRM, signal_number);
|
||||||
@@ -321,6 +327,7 @@ TEST(time, timer_create_NULL) {
|
|||||||
timer_t timer_id;
|
timer_t timer_id;
|
||||||
ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &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);
|
ScopedSignalHandler ssh(SIGALRM, timer_create_NULL_signal_handler);
|
||||||
|
|
||||||
ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count);
|
ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count);
|
||||||
@@ -367,22 +374,59 @@ TEST(time, timer_delete_multiple) {
|
|||||||
|
|
||||||
TEST(time, timer_create_multiple) {
|
TEST(time, timer_create_multiple) {
|
||||||
Counter counter1(Counter::CountNotifyFunction);
|
Counter counter1(Counter::CountNotifyFunction);
|
||||||
ASSERT_TRUE(counter1.timer_valid);
|
|
||||||
Counter counter2(Counter::CountNotifyFunction);
|
Counter counter2(Counter::CountNotifyFunction);
|
||||||
ASSERT_TRUE(counter2.timer_valid);
|
|
||||||
Counter counter3(Counter::CountNotifyFunction);
|
Counter counter3(Counter::CountNotifyFunction);
|
||||||
ASSERT_TRUE(counter3.timer_valid);
|
|
||||||
|
|
||||||
ASSERT_EQ(0, counter1.value);
|
ASSERT_EQ(0, counter1.Value());
|
||||||
ASSERT_EQ(0, counter2.value);
|
ASSERT_EQ(0, counter2.Value());
|
||||||
ASSERT_EQ(0, counter3.value);
|
ASSERT_EQ(0, counter3.Value());
|
||||||
|
|
||||||
counter2.SetTime(0, 1, 0, 0);
|
counter2.SetTime(0, 1, 0, 0);
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
|
|
||||||
EXPECT_EQ(0, counter1.value);
|
EXPECT_EQ(0, counter1.Value());
|
||||||
EXPECT_EQ(1, counter2.value);
|
EXPECT_EQ(1, counter2.Value());
|
||||||
EXPECT_EQ(0, counter3.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 {
|
struct TimerDeleteData {
|
||||||
@@ -499,45 +543,3 @@ TEST(time, clock_nanosleep) {
|
|||||||
timespec out;
|
timespec out;
|
||||||
ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &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
|
* Python 2.7
|
||||||
|
* [Advanced Python Scheduler](https://apscheduler.readthedocs.org/en/latest/)
|
||||||
* [Flask](http://flask.pocoo.org/)
|
* [Flask](http://flask.pocoo.org/)
|
||||||
* [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation)
|
* [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation)
|
||||||
* [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi)
|
* [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi)
|
||||||
* [Requests](http://docs.python-requests.org/en/latest/)
|
* [Requests](http://docs.python-requests.org/en/latest/)
|
||||||
* [termcolor](https://pypi.python.org/pypi/termcolor)
|
|
||||||
|
|
||||||
Setup
|
Setup
|
||||||
-----
|
-----
|
||||||
|
|||||||
@@ -15,12 +15,16 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
from flask import Flask, request
|
||||||
import requests
|
import requests
|
||||||
import termcolor
|
|
||||||
|
|
||||||
import gerrit
|
import gerrit
|
||||||
|
import tasks
|
||||||
|
|
||||||
from flask import Flask, request
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -43,7 +47,7 @@ def handle_build_message():
|
|||||||
ref = params['REF']
|
ref = params['REF']
|
||||||
patch_set = ref.split('/')[-1]
|
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
|
# bionic-lint is always broken, so we don't want to reject changes for
|
||||||
# those failures until we clean things up.
|
# those failures until we clean things up.
|
||||||
@@ -69,19 +73,19 @@ def handle_build_message():
|
|||||||
patch_set))
|
patch_set))
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||||
print 'POST {}: {}'.format(url, request_data)
|
logging.debug('POST %s: %s', url, request_data)
|
||||||
print requests.post(url, headers=headers, json=request_data)
|
requests.post(url, headers=headers, json=request_data)
|
||||||
elif name == 'clean-bionic-presubmit':
|
elif name == 'clean-bionic-presubmit':
|
||||||
request_data = {'message': 'out/ directory removed'}
|
request_data = {'message': 'out/ directory removed'}
|
||||||
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
||||||
patch_set))
|
patch_set))
|
||||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||||
print 'POST {}: {}'.format(url, request_data)
|
logging.debug('POST %s: %s', url, request_data)
|
||||||
print requests.post(url, headers=headers, json=request_data)
|
requests.post(url, headers=headers, json=request_data)
|
||||||
elif name == 'bionic-lint':
|
elif name == 'bionic-lint':
|
||||||
print 'IGNORED'
|
logging.warning('Result for bionic-lint ignored')
|
||||||
else:
|
else:
|
||||||
print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name)
|
logging.error('Unknown project: %s', name)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@@ -100,19 +104,31 @@ def drop_rejection():
|
|||||||
bb_review = 0
|
bb_review = 0
|
||||||
|
|
||||||
if 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 ''
|
return ''
|
||||||
|
|
||||||
print 'Dropping rejection: {} {}'.format(change_id, patch_set)
|
logging.info('Dropping rejection: %s %s', change_id, patch_set)
|
||||||
|
|
||||||
request_data = {'labels': {'Verified': 0}}
|
request_data = {'labels': {'Verified': 0}}
|
||||||
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
|
||||||
patch_set))
|
patch_set))
|
||||||
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
headers = {'Content-Type': 'application/json;charset=UTF-8'}
|
||||||
print 'POST {}: {}'.format(url, request_data)
|
logging.debug('POST %s: %s', url, request_data)
|
||||||
print requests.post(url, headers=headers, json=request_data)
|
requests.post(url, headers=headers, json=request_data)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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)
|
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)))
|
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'):
|
def call(endpoint, method='GET'):
|
||||||
if method != 'GET':
|
if method != 'GET':
|
||||||
raise NotImplementedError('Currently only HTTP GET is supported.')
|
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/debug.cc \
|
||||||
src/delta_encoder.cc \
|
src/delta_encoder.cc \
|
||||||
src/elf_file.cc \
|
src/elf_file.cc \
|
||||||
src/leb128.cc \
|
|
||||||
src/packer.cc \
|
src/packer.cc \
|
||||||
src/sleb128.cc \
|
src/sleb128.cc \
|
||||||
|
|
||||||
@@ -46,6 +45,9 @@ LOCAL_CPP_EXTENSION := .cc
|
|||||||
|
|
||||||
LOCAL_SRC_FILES := src/main.cc
|
LOCAL_SRC_FILES := src/main.cc
|
||||||
LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
|
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_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
|
||||||
|
|
||||||
LOCAL_MODULE := relocation_packer
|
LOCAL_MODULE := relocation_packer
|
||||||
@@ -64,7 +66,6 @@ LOCAL_SRC_FILES := \
|
|||||||
src/debug_unittest.cc \
|
src/debug_unittest.cc \
|
||||||
src/delta_encoder_unittest.cc \
|
src/delta_encoder_unittest.cc \
|
||||||
src/elf_file_unittest.cc \
|
src/elf_file_unittest.cc \
|
||||||
src/leb128_unittest.cc \
|
|
||||||
src/sleb128_unittest.cc \
|
src/sleb128_unittest.cc \
|
||||||
src/packer_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