Merge "Start documenting libc."
diff --git a/benchmarks/string_benchmark.cpp b/benchmarks/string_benchmark.cpp
index 38122f2..d176675 100644
--- a/benchmarks/string_benchmark.cpp
+++ b/benchmarks/string_benchmark.cpp
@@ -248,6 +248,25 @@
}
BIONIC_BENCHMARK_WITH_ARG(BM_string_strcmp, "AT_ALIGNED_TWOBUF");
+static void BM_string_strncmp(benchmark::State& state) {
+ const size_t nbytes = state.range(0);
+ const size_t s1_alignment = state.range(1);
+ const size_t s2_alignment = state.range(2);
+
+ std::vector<char> s1;
+ std::vector<char> s2;
+ char* s1_aligned = GetAlignedPtrFilled(&s1, s1_alignment, nbytes, 'x');
+ char* s2_aligned = GetAlignedPtrFilled(&s2, s2_alignment, nbytes, 'x');
+
+ volatile int c __attribute__((unused));
+ for (auto _ : state) {
+ c = strncmp(s1_aligned, s2_aligned, nbytes);
+ }
+
+ state.SetBytesProcessed(uint64_t(state.iterations()) * uint64_t(nbytes));
+}
+BIONIC_BENCHMARK_WITH_ARG(BM_string_strncmp, "AT_ALIGNED_TWOBUF");
+
static void BM_string_strstr(benchmark::State& state) {
const size_t nbytes = state.range(0);
const size_t haystack_alignment = state.range(1);
diff --git a/libc/Android.bp b/libc/Android.bp
index 7356c64..61d00cd 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -71,7 +71,8 @@
stl: "none",
system_shared_libs: [],
sanitize: {
- never: true,
+ address: false,
+ integer_overflow: false,
},
native_coverage: false,
recovery_available: true,
diff --git a/libc/arch-arm64/generic/bionic/memcmp.S b/libc/arch-arm64/generic/bionic/memcmp.S
index 3a138bf..bff54ae 100644
--- a/libc/arch-arm64/generic/bionic/memcmp.S
+++ b/libc/arch-arm64/generic/bionic/memcmp.S
@@ -33,6 +33,8 @@
#include <private/bionic_asm.h>
+#define L(l) .L ## l
+
/* Parameters and result. */
#define src1 x0
#define src2 x1
@@ -42,88 +44,124 @@
/* Internal variables. */
#define data1 x3
#define data1w w3
-#define data2 x4
-#define data2w w4
-#define tmp1 x5
+#define data1h x4
+#define data2 x5
+#define data2w w5
+#define data2h x6
+#define tmp1 x7
+#define tmp2 x8
/* Small inputs of less than 8 bytes are handled separately. This allows the
- main code to be sped up using unaligned loads since there are now at least
+ main code to be speed up using unaligned loads since there are now at least
8 bytes to be compared. If the first 8 bytes are equal, align src1.
This ensures each iteration does at most one unaligned access even if both
src1 and src2 are unaligned, and mutually aligned inputs behave as if
- aligned. After the main loop, process the last 8 bytes using unaligned
+ aligned. After the main loop, process the last 16 bytes using unaligned
accesses. */
-.p2align 6
ENTRY(memcmp)
+.p2align 6
subs limit, limit, 8
- b.lo .Lless8
+ b.lo L(less8)
/* Limit >= 8, so check first 8 bytes using unaligned loads. */
ldr data1, [src1], 8
ldr data2, [src2], 8
- and tmp1, src1, 7
- add limit, limit, tmp1
cmp data1, data2
- bne .Lreturn
+ b.ne L(return)
+
+ subs limit, limit, 8
+ b.gt L(more16)
+
+ ldr data1, [src1, limit]
+ ldr data2, [src2, limit]
+ b L(return)
+
+L(more16):
+ ldr data1, [src1], 8
+ ldr data2, [src2], 8
+ cmp data1, data2
+ bne L(return)
+
+ /* Jump directly to comparing the last 16 bytes for 32 byte (or less)
+ strings. */
+ subs limit, limit, 16
+ b.ls L(last_bytes)
+
+ /* We overlap loads between 0-32 bytes at either side of SRC1 when we
+ try to align, so limit it only to strings larger than 128 bytes. */
+ cmp limit, 96
+ b.ls L(loop16)
/* Align src1 and adjust src2 with bytes not yet done. */
+ and tmp1, src1, 15
+ add limit, limit, tmp1
sub src1, src1, tmp1
sub src2, src2, tmp1
- subs limit, limit, 8
- b.ls .Llast_bytes
-
- /* Loop performing 8 bytes per iteration using aligned src1.
- Limit is pre-decremented by 8 and must be larger than zero.
- Exit if <= 8 bytes left to do or if the data is not equal. */
+ /* Loop performing 16 bytes per iteration using aligned src1.
+ Limit is pre-decremented by 16 and must be larger than zero.
+ Exit if <= 16 bytes left to do or if the data is not equal. */
.p2align 4
-.Lloop8:
- ldr data1, [src1], 8
- ldr data2, [src2], 8
- subs limit, limit, 8
- ccmp data1, data2, 0, hi /* NZCV = 0b0000. */
- b.eq .Lloop8
+L(loop16):
+ ldp data1, data1h, [src1], 16
+ ldp data2, data2h, [src2], 16
+ subs limit, limit, 16
+ ccmp data1, data2, 0, hi
+ ccmp data1h, data2h, 0, eq
+ b.eq L(loop16)
cmp data1, data2
- bne .Lreturn
+ bne L(return)
+ mov data1, data1h
+ mov data2, data2h
+ cmp data1, data2
+ bne L(return)
- /* Compare last 1-8 bytes using unaligned access. */
-.Llast_bytes:
- ldr data1, [src1, limit]
- ldr data2, [src2, limit]
+ /* Compare last 1-16 bytes using unaligned access. */
+L(last_bytes):
+ add src1, src1, limit
+ add src2, src2, limit
+ ldp data1, data1h, [src1]
+ ldp data2, data2h, [src2]
+ cmp data1, data2
+ bne L(return)
+ mov data1, data1h
+ mov data2, data2h
+ cmp data1, data2
/* Compare data bytes and set return value to 0, -1 or 1. */
-.Lreturn:
+L(return):
#ifndef __AARCH64EB__
rev data1, data1
rev data2, data2
#endif
cmp data1, data2
-.Lret_eq:
+L(ret_eq):
cset result, ne
cneg result, result, lo
- ret
+ ret
.p2align 4
/* Compare up to 8 bytes. Limit is [-8..-1]. */
-.Lless8:
+L(less8):
adds limit, limit, 4
- b.lo .Lless4
+ b.lo L(less4)
ldr data1w, [src1], 4
ldr data2w, [src2], 4
cmp data1w, data2w
- b.ne .Lreturn
+ b.ne L(return)
sub limit, limit, 4
-.Lless4:
+L(less4):
adds limit, limit, 4
- beq .Lret_eq
-.Lbyte_loop:
+ beq L(ret_eq)
+L(byte_loop):
ldrb data1w, [src1], 1
ldrb data2w, [src2], 1
subs limit, limit, 1
ccmp data1w, data2w, 0, ne /* NZCV = 0b0000. */
- b.eq .Lbyte_loop
+ b.eq L(byte_loop)
sub result, data1w, data2w
ret
+
END(memcmp)
diff --git a/libc/arch-arm64/generic/bionic/strcmp.S b/libc/arch-arm64/generic/bionic/strcmp.S
index 271452d..fbc215e 100644
--- a/libc/arch-arm64/generic/bionic/strcmp.S
+++ b/libc/arch-arm64/generic/bionic/strcmp.S
@@ -32,6 +32,8 @@
#include <private/bionic_asm.h>
+#define L(label) .L ## label
+
#define REP8_01 0x0101010101010101
#define REP8_7f 0x7f7f7f7f7f7f7f7f
#define REP8_80 0x8080808080808080
@@ -61,24 +63,25 @@
eor tmp1, src1, src2
mov zeroones, #REP8_01
tst tmp1, #7
- b.ne .Lmisaligned8
+ b.ne L(misaligned8)
ands tmp1, src1, #7
- b.ne .Lmutual_align
+ b.ne L(mutual_align)
/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
(=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
can be done in parallel across the entire word. */
-.Lloop_aligned:
+L(loop_aligned):
ldr data1, [src1], #8
ldr data2, [src2], #8
-.Lstart_realigned:
+L(start_realigned):
sub tmp1, data1, zeroones
orr tmp2, data1, #REP8_7f
eor diff, data1, data2 /* Non-zero if differences found. */
bic has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
orr syndrome, diff, has_nul
- cbz syndrome, .Lloop_aligned
+ cbz syndrome, L(loop_aligned)
/* End of performance-critical section -- one 64B cache line. */
+L(end):
#ifndef __AARCH64EB__
rev syndrome, syndrome
rev data1, data1
@@ -129,7 +132,7 @@
ret
#endif
-.Lmutual_align:
+L(mutual_align):
/* Sources are mutually aligned, but are not currently at an
alignment boundary. Round down the addresses and then mask off
the bytes that preceed the start point. */
@@ -149,15 +152,41 @@
#endif
orr data1, data1, tmp2
orr data2, data2, tmp2
- b .Lstart_realigned
+ b L(start_realigned)
-.Lmisaligned8:
- /* We can do better than this. */
+L(misaligned8):
+ /* Align SRC1 to 8 bytes and then compare 8 bytes at a time, always
+ checking to make sure that we don't access beyond page boundary in
+ SRC2. */
+ tst src1, #7
+ b.eq L(loop_misaligned)
+L(do_misaligned):
ldrb data1w, [src1], #1
ldrb data2w, [src2], #1
cmp data1w, #1
ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
- b.eq .Lmisaligned8
+ b.ne L(done)
+ tst src1, #7
+ b.ne L(do_misaligned)
+
+L(loop_misaligned):
+ /* Test if we are within the last dword of the end of a 4K page. If
+ yes then jump back to the misaligned loop to copy a byte at a time. */
+ and tmp1, src2, #0xff8
+ eor tmp1, tmp1, #0xff8
+ cbz tmp1, L(do_misaligned)
+ ldr data1, [src1], #8
+ ldr data2, [src2], #8
+
+ sub tmp1, data1, zeroones
+ orr tmp2, data1, #REP8_7f
+ eor diff, data1, data2 /* Non-zero if differences found. */
+ bic has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
+ orr syndrome, diff, has_nul
+ cbz syndrome, L(loop_misaligned)
+ b L(end)
+
+L(done):
sub result, data1, data2
ret
END(strcmp)
diff --git a/libc/arch-arm64/generic/bionic/strncmp.S b/libc/arch-arm64/generic/bionic/strncmp.S
index 267f663..b81f43a 100644
--- a/libc/arch-arm64/generic/bionic/strncmp.S
+++ b/libc/arch-arm64/generic/bionic/strncmp.S
@@ -58,6 +58,7 @@
#define limit_wd x13
#define mask x14
#define endloop x15
+#define count mask
.text
.p2align 6
@@ -69,9 +70,9 @@
eor tmp1, src1, src2
mov zeroones, #REP8_01
tst tmp1, #7
+ and count, src1, #7
b.ne .Lmisaligned8
- ands tmp1, src1, #7
- b.ne .Lmutual_align
+ cbnz count, .Lmutual_align
/* Calculate the number of full and partial words -1. */
sub limit_wd, limit, #1 /* limit != 0, so no underflow. */
lsr limit_wd, limit_wd, #3 /* Convert to Dwords. */
@@ -176,42 +177,104 @@
bic src1, src1, #7
bic src2, src2, #7
ldr data1, [src1], #8
- neg tmp3, tmp1, lsl #3 /* 64 - bits(bytes beyond align). */
+ neg tmp3, count, lsl #3 /* 64 - bits(bytes beyond align). */
ldr data2, [src2], #8
mov tmp2, #~0
sub limit_wd, limit, #1 /* limit != 0, so no underflow. */
#ifdef __AARCH64EB__
/* Big-endian. Early bytes are at MSB. */
- lsl tmp2, tmp2, tmp3 /* Shift (tmp1 & 63). */
+ lsl tmp2, tmp2, tmp3 /* Shift (count & 63). */
#else
/* Little-endian. Early bytes are at LSB. */
- lsr tmp2, tmp2, tmp3 /* Shift (tmp1 & 63). */
+ lsr tmp2, tmp2, tmp3 /* Shift (count & 63). */
#endif
and tmp3, limit_wd, #7
lsr limit_wd, limit_wd, #3
/* Adjust the limit. Only low 3 bits used, so overflow irrelevant. */
- add limit, limit, tmp1
- add tmp3, tmp3, tmp1
+ add limit, limit, count
+ add tmp3, tmp3, count
orr data1, data1, tmp2
orr data2, data2, tmp2
add limit_wd, limit_wd, tmp3, lsr #3
b .Lstart_realigned
-.Lret0:
- mov result, #0
- ret
-
.p2align 6
+ /* Don't bother with dwords for up to 16 bytes. */
.Lmisaligned8:
- sub limit, limit, #1
-1:
+ cmp limit, #16
+ b.hs .Ltry_misaligned_words
+
+.Lbyte_loop:
/* Perhaps we can do better than this. */
ldrb data1w, [src1], #1
ldrb data2w, [src2], #1
subs limit, limit, #1
- ccmp data1w, #1, #0, cs /* NZCV = 0b0000. */
+ ccmp data1w, #1, #0, hi /* NZCV = 0b0000. */
ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
- b.eq 1b
+ b.eq .Lbyte_loop
+.Ldone:
sub result, data1, data2
ret
+ /* Align the SRC1 to a dword by doing a bytewise compare and then do
+ the dword loop. */
+.Ltry_misaligned_words:
+ lsr limit_wd, limit, #3
+ cbz count, .Ldo_misaligned
+
+ neg count, count
+ and count, count, #7
+ sub limit, limit, count
+ lsr limit_wd, limit, #3
+
+.Lpage_end_loop:
+ ldrb data1w, [src1], #1
+ ldrb data2w, [src2], #1
+ cmp data1w, #1
+ ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
+ b.ne .Ldone
+ subs count, count, #1
+ b.hi .Lpage_end_loop
+
+.Ldo_misaligned:
+ /* Prepare ourselves for the next page crossing. Unlike the aligned
+ loop, we fetch 1 less dword because we risk crossing bounds on
+ SRC2. */
+ mov count, #8
+ subs limit_wd, limit_wd, #1
+ b.lo .Ldone_loop
+.Lloop_misaligned:
+ and tmp2, src2, #0xff8
+ eor tmp2, tmp2, #0xff8
+ cbz tmp2, .Lpage_end_loop
+
+ ldr data1, [src1], #8
+ ldr data2, [src2], #8
+ sub tmp1, data1, zeroones
+ orr tmp2, data1, #REP8_7f
+ eor diff, data1, data2 /* Non-zero if differences found. */
+ bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
+ ccmp diff, #0, #0, eq
+ b.ne .Lnot_limit
+ subs limit_wd, limit_wd, #1
+ b.pl .Lloop_misaligned
+
+.Ldone_loop:
+ /* We found a difference or a NULL before the limit was reached. */
+ and limit, limit, #7
+ cbz limit, .Lnot_limit
+ /* Read the last word. */
+ sub src1, src1, 8
+ sub src2, src2, 8
+ ldr data1, [src1, limit]
+ ldr data2, [src2, limit]
+ sub tmp1, data1, zeroones
+ orr tmp2, data1, #REP8_7f
+ eor diff, data1, data2 /* Non-zero if differences found. */
+ bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
+ ccmp diff, #0, #0, eq
+ b.ne .Lnot_limit
+
+.Lret0:
+ mov result, #0
+ ret
END(strncmp)
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 9eb574a..55506a3 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -45,6 +45,10 @@
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
+#if __has_feature(hwaddress_sanitizer)
+#include <sanitizer/hwasan_interface.h>
+#endif
+
// Leave the variable uninitialized for the sake of the dynamic loader, which
// links in this file. The loader will initialize this variable before
// relocating itself.
@@ -85,11 +89,10 @@
//
// The 'structors' parameter contains pointers to various initializer
// arrays that must be run before the program's 'main' routine is launched.
-
-__noreturn void __libc_init(void* raw_args,
- void (*onexit)(void) __unused,
- int (*slingshot)(int, char**, char**),
- structors_array_t const * const structors) {
+__noreturn static void __real_libc_init(void *raw_args,
+ void (*onexit)(void) __unused,
+ int (*slingshot)(int, char**, char**),
+ structors_array_t const * const structors) {
BIONIC_STOP_UNWIND;
KernelArgumentBlock args(raw_args);
@@ -124,6 +127,20 @@
exit(slingshot(args.argc, args.argv, args.envp));
}
+#if __has_feature(hwaddress_sanitizer)
+__attribute__((no_sanitize("hwaddress")))
+#endif
+__noreturn void __libc_init(void* raw_args,
+ void (*onexit)(void) __unused,
+ int (*slingshot)(int, char**, char**),
+ structors_array_t const * const structors) {
+#if __has_feature(hwaddress_sanitizer)
+ __hwasan_shadow_init();
+#endif
+ __real_libc_init(raw_args, onexit, slingshot, structors);
+}
+
+
static uint32_t g_target_sdk_version{__ANDROID_API__};
extern "C" uint32_t android_get_application_target_sdk_version() {
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 40a0023..5a5ec76 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -47,8 +47,26 @@
#include <private/bionic_globals.h>
#include <private/bionic_malloc_dispatch.h>
+#if __has_feature(hwaddress_sanitizer)
+// FIXME: implement these in HWASan allocator.
+extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
+ void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
+ void* arg __unused) {
+ return 0;
+}
+
+extern "C" void __sanitizer_malloc_disable() {
+}
+
+extern "C" void __sanitizer_malloc_enable() {
+}
+#include <sanitizer/hwasan_interface.h>
+#define Malloc(function) __sanitizer_ ## function
+
+#else // __has_feature(hwaddress_sanitizer)
#include "jemalloc.h"
#define Malloc(function) je_ ## function
+#endif
static constexpr MallocDispatch __libc_malloc_default_dispatch
__attribute__((unused)) = {
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index c95d400..15366af 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -31,6 +31,7 @@
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <unistd.h>
#include "pthread_internal.h"
@@ -39,7 +40,6 @@
#include "private/bionic_defs.h"
#include "private/bionic_macros.h"
-#include "private/bionic_prctl.h"
#include "private/bionic_ssp.h"
#include "private/bionic_tls.h"
#include "private/ErrnoRestorer.h"
@@ -249,6 +249,8 @@
// accesses previously made by the creating thread are visible to us.
thread->startup_handshake_lock.lock();
+ __hwasan_thread_enter();
+
__init_alternate_signal_stack(thread);
void* result = thread->start_routine(thread->start_routine_arg);
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index ac5d429..220f7a0 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -126,6 +126,7 @@
// That's one last thing we can do before dropping to assembler.
ScopedSignalBlocker ssb;
__pthread_unmap_tls(thread);
+ __hwasan_thread_exit();
_exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
}
}
@@ -133,5 +134,6 @@
// No need to free mapped space. Either there was no space mapped, or it is left for
// the pthread_join caller to clean up.
__pthread_unmap_tls(thread);
+ __hwasan_thread_exit();
__exit(0);
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 18f5aee..1ec201b 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -31,6 +31,13 @@
#include <pthread.h>
#include <stdatomic.h>
+#if __has_feature(hwaddress_sanitizer)
+#include <sanitizer/hwasan_interface.h>
+#else
+#define __hwasan_thread_enter()
+#define __hwasan_thread_exit()
+#endif
+
#include "private/bionic_lock.h"
#include "private/bionic_tls.h"
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index 7a909d7..6ed6c0a 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -59,5 +59,6 @@
#define __ANDROID_API_O__ 26
#define __ANDROID_API_O_MR1__ 27
#define __ANDROID_API_P__ 28
+#define __ANDROID_API_Q__ 29
#endif
diff --git a/libc/include/sys/prctl.h b/libc/include/sys/prctl.h
index 64f5954..bd42411 100644
--- a/libc/include/sys/prctl.h
+++ b/libc/include/sys/prctl.h
@@ -26,17 +26,40 @@
* SUCH DAMAGE.
*/
-#ifndef _SYS_PRCTL_H
-#define _SYS_PRCTL_H
+#pragma once
+
+/**
+ * @file sys/prctl.h
+ * @brief Process-specific operations.
+ */
#include <sys/cdefs.h>
#include <linux/prctl.h>
+/**
+ * Names a VMA (mmap'ed region). The second argument must be `PR_SET_VMA_ANON_NAME`,
+ * the third and fourth are a `void*` pointer to the VMA and its `size_t` length in
+ * bytes, and the final argument is a `const char*` pointer to the name.
+ *
+ * Note that the kernel keeps the pointer to the name rather than copying the name,
+ * so the lifetime of the string should be at least as long as that of the VMA.
+ */
+#define PR_SET_VMA 0x53564d41
+
+/**
+ * For use with `PR_SET_VMA`.
+ */
+#define PR_SET_VMA_ANON_NAME 0
+
__BEGIN_DECLS
+/**
+ * [prctl(2)](http://man7.org/linux/man-pages/man2/prctl.2.html) performs a variety of
+ * operations based on the `PR_` constant passed as the first argument.
+ *
+ * Returns -1 and sets `errno` on failure; success values vary by option.
+ */
int prctl(int __option, ...);
__END_DECLS
-
-#endif
diff --git a/libc/private/WriteProtected.h b/libc/private/WriteProtected.h
index 20afdec..7a6b098 100644
--- a/libc/private/WriteProtected.h
+++ b/libc/private/WriteProtected.h
@@ -26,7 +26,6 @@
#include <async_safe/log.h>
#include "private/bionic_macros.h"
-#include "private/bionic_prctl.h"
template <typename T>
union WriteProtectedContents {
diff --git a/libc/private/bionic_prctl.h b/libc/private/bionic_prctl.h
deleted file mode 100644
index 103cccb..0000000
--- a/libc/private/bionic_prctl.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef BIONIC_PRCTL_H
-#define BIONIC_PRCTL_H
-
-#include <sys/prctl.h>
-
-// This is only supported by Android kernels, so it's not in the uapi headers.
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
-
-#endif // BIONIC_PRCTL_H
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index a26bee4..bd6ac3d 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -36,12 +36,9 @@
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/types.h>
-/* BEGIN android-changed */
-#include "private/bionic_prctl.h"
-/* END android-changed */
-
static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
#define _ATEXIT_LOCK() pthread_mutex_lock(&g_atexit_lock)
#define _ATEXIT_UNLOCK() pthread_mutex_unlock(&g_atexit_lock)
diff --git a/libc/system_properties/contexts_serialized.cpp b/libc/system_properties/contexts_serialized.cpp
index 12e9715..6ccd46c 100644
--- a/libc/system_properties/contexts_serialized.cpp
+++ b/libc/system_properties/contexts_serialized.cpp
@@ -31,6 +31,7 @@
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -38,7 +39,6 @@
#include <async_safe/log.h>
-#include "private/bionic_prctl.h"
#include "system_properties/system_properties.h"
bool ContextsSerialized::InitializeContextNodes() {
diff --git a/libc/upstream-openbsd/android/include/arc4random.h b/libc/upstream-openbsd/android/include/arc4random.h
index afa8d14..5f0b15e 100644
--- a/libc/upstream-openbsd/android/include/arc4random.h
+++ b/libc/upstream-openbsd/android/include/arc4random.h
@@ -26,11 +26,10 @@
#include <pthread.h>
#include <signal.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <async_safe/log.h>
-#include "private/bionic_prctl.h"
-
// Android gets these from "thread_private.h".
#include "thread_private.h"
//static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
diff --git a/libm/Android.bp b/libm/Android.bp
index da13ab1..3b88fa3 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -493,6 +493,7 @@
"-D_BSD_SOURCE",
"-DFLT_EVAL_METHOD=0",
"-include freebsd-compat.h",
+ "-fno-math-errno",
"-Wall",
"-Werror",
"-Wno-missing-braces",
@@ -503,6 +504,10 @@
"-Wno-unused-variable",
],
+ ldflags: [
+ "-Wl,--Bsymbolic-functions",
+ ],
+
include_dirs: ["bionic/libc"],
system_shared_libs: ["libc"],
diff --git a/linker/Android.bp b/linker/Android.bp
index fb6aa7d..b809f76 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -228,6 +228,10 @@
// 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_",
+
+ sanitize: {
+ hwaddress: false,
+ },
}
cc_library {
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 4b84537..dfe8e8c 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -303,9 +303,7 @@
static soinfo* __libdl_info = nullptr;
// This is used by the dynamic linker. Every process gets these symbols for free.
-soinfo* get_libdl_info(const char* linker_path,
- const soinfo& linker_si,
- const link_map& linker_map) {
+soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si) {
CHECK((linker_si.flags_ & FLAG_GNU_HASH) != 0);
if (__libdl_info == nullptr) {
@@ -314,6 +312,8 @@
__libdl_info->strtab_ = linker_si.strtab_;
__libdl_info->symtab_ = linker_si.symtab_;
__libdl_info->load_bias = linker_si.load_bias;
+ __libdl_info->phdr = linker_si.phdr;
+ __libdl_info->phnum = linker_si.phnum;
__libdl_info->gnu_nbucket_ = linker_si.gnu_nbucket_;
__libdl_info->gnu_maskwords_ = linker_si.gnu_maskwords_;
@@ -328,9 +328,6 @@
__libdl_info->soname_ = linker_si.soname_;
__libdl_info->target_sdk_version_ = __ANDROID_API__;
__libdl_info->generate_handle();
- __libdl_info->link_map_head.l_addr = linker_map.l_addr;
- __libdl_info->link_map_head.l_name = linker_map.l_name;
- __libdl_info->link_map_head.l_ld = linker_map.l_ld;
#if defined(__work_around_b_24465209__)
strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_));
#endif
diff --git a/linker/linker.h b/linker/linker.h
index 260c89d..7aa7e5f 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -99,9 +99,7 @@
void count_relocation(RelocationKind kind);
-soinfo* get_libdl_info(const char* linker_path,
- const soinfo& linker_si,
- const link_map& linker_map);
+soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si);
soinfo* find_containing_library(const void* p);
diff --git a/linker/linker_allocator.cpp b/linker/linker_allocator.cpp
index a37e910..de3309b 100644
--- a/linker/linker_allocator.cpp
+++ b/linker/linker_allocator.cpp
@@ -35,12 +35,11 @@
#include <stdlib.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <unistd.h>
#include <async_safe/log.h>
-#include "private/bionic_prctl.h"
-
//
// LinkerMemeoryAllocator is general purpose allocator
// designed to provide the same functionality as the malloc/free/realloc
diff --git a/linker/linker_allocator.h b/linker/linker_allocator.h
index 82e4ef4..8c4198b 100644
--- a/linker/linker_allocator.h
+++ b/linker/linker_allocator.h
@@ -32,6 +32,7 @@
#include <stdlib.h>
#include <sys/cdefs.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <stddef.h>
#include <unistd.h>
@@ -39,8 +40,6 @@
#include <async_safe/log.h>
-#include "private/bionic_prctl.h"
-
const uint32_t kSmallObjectMaxSizeLog2 = 10;
const uint32_t kSmallObjectMinSizeLog2 = 4;
const uint32_t kSmallObjectAllocatorsCount = kSmallObjectMaxSizeLog2 - kSmallObjectMinSizeLog2 + 1;
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp
index abb1ebd..dca944e 100644
--- a/linker/linker_block_allocator.cpp
+++ b/linker/linker_block_allocator.cpp
@@ -30,10 +30,9 @@
#include <inttypes.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <unistd.h>
-#include "private/bionic_prctl.h"
-
// the multiplier should be power of 2
static constexpr size_t round_up(size_t size, size_t multiplier) {
return (size + (multiplier - 1)) & ~(multiplier-1);
diff --git a/linker/linker_cfi.cpp b/linker/linker_cfi.cpp
index 8910c3f..782ebc6 100644
--- a/linker/linker_cfi.cpp
+++ b/linker/linker_cfi.cpp
@@ -31,9 +31,9 @@
#include "linker_debug.h"
#include "linker_globals.h"
#include "private/bionic_page.h"
-#include "private/bionic_prctl.h"
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <cstdint>
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 3410f90..c5e0b96 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -67,6 +67,7 @@
static soinfo* solist;
static soinfo* sonext;
static soinfo* somain; // main process, always the one after libdl_info
+static soinfo* solinker;
static soinfo* vdso; // vdso if present
void solist_add_soinfo(soinfo* si) {
@@ -170,29 +171,13 @@
vdso = si;
}
-/* gdb expects the linker to be in the debug shared object list.
- * Without this, gdb has trouble locating the linker's ".text"
- * and ".plt" sections. Gdb could also potentially use this to
- * relocate the offset of our exported 'rtld_db_dlactivity' symbol.
- * Note that the linker shouldn't be on the soinfo list.
- */
-static link_map linker_link_map;
-
-static void init_linker_info_for_gdb(ElfW(Addr) linker_base, char* linker_path) {
- linker_link_map.l_addr = linker_base;
- linker_link_map.l_name = linker_path;
-
- /*
- * Set the dynamic field in the link map otherwise gdb will complain with
- * the following:
- * warning: .dynamic section for "/system/bin/linker" is not at the
- * expected address (wrong library or version mismatch?)
- */
- ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_base);
- ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_base + elf_hdr->e_phoff);
- phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base,
- &linker_link_map.l_ld, nullptr);
-
+// Initializes an soinfo's link_map_head field using other fields from the
+// soinfo (phdr, phnum, load_bias).
+static void init_link_map_head(soinfo& info, const char* linker_path) {
+ auto& map = info.link_map_head;
+ map.l_addr = info.load_bias;
+ map.l_name = const_cast<char*>(linker_path);
+ phdr_table_get_dynamic_section(info.phdr, info.phnum, info.load_bias, &map.l_ld, nullptr);
}
extern "C" int __system_properties_init(void);
@@ -289,8 +274,6 @@
}
}
- add_vdso(args);
-
struct stat file_stat;
// Stat "/proc/self/exe" instead of executable_path because
// the executable could be unlinked by this point and it should
@@ -305,29 +288,31 @@
stat("/init", &file_stat);
}
+ // Initialize the main exe's soinfo.
const char* executable_path = get_executable_path();
soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL);
-
- // Bootstrap the link map, the main exe always needs to be first.
+ somain = si;
+ si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR));
+ si->phnum = args.getauxval(AT_PHNUM);
+ get_elf_base_from_phdr(si->phdr, si->phnum, &si->base, &si->load_bias);
+ si->size = phdr_table_get_load_size(si->phdr, si->phnum);
+ si->dynamic = nullptr;
si->set_main_executable();
- link_map* map = &(si->link_map_head);
+ init_link_map_head(*si, executable_path);
// Register the main executable and the linker upfront to have
// gdb aware of them before loading the rest of the dependency
// tree.
- map->l_addr = 0;
- map->l_name = const_cast<char*>(executable_path);
- insert_link_map_into_debug_map(map);
- insert_link_map_into_debug_map(&linker_link_map);
+ //
+ // gdb expects the linker to be in the debug shared object list.
+ // Without this, gdb has trouble locating the linker's ".text"
+ // and ".plt" sections. Gdb could also potentially use this to
+ // relocate the offset of our exported 'rtld_db_dlactivity' symbol.
+ //
+ insert_link_map_into_debug_map(&si->link_map_head);
+ insert_link_map_into_debug_map(&solinker->link_map_head);
- // Extract information passed from the kernel.
- si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR));
- si->phnum = args.getauxval(AT_PHNUM);
-
- get_elf_base_from_phdr(si->phdr, si->phnum, &si->base, &si->load_bias);
- si->size = phdr_table_get_load_size(si->phdr, si->phnum);
-
- si->dynamic = nullptr;
+ add_vdso(args);
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
@@ -351,8 +336,6 @@
parse_LD_LIBRARY_PATH(ldpath_env);
parse_LD_PRELOAD(ldpreload_env);
- somain = si;
-
std::vector<android_namespace_t*> namespaces = init_default_namespaces(executable_path);
if (!si->prelink_image()) __linker_cannot_link(g_argv[0]);
@@ -411,13 +394,6 @@
__get_tls()[TLS_SLOT_BIONIC_PREINIT] = &args;
si->call_pre_init_constructors();
-
- /* After the prelink_image, the si->load_bias is initialized.
- * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
- * We need to update this value for so exe here. So Unwind_Backtrace
- * for some arch like x86 could work correctly within so exe.
- */
- map->l_addr = si->load_bias;
si->call_constructors();
#if TIMING
@@ -508,9 +484,7 @@
}
static ElfW(Addr) __attribute__((noinline))
-__linker_init_post_relocation(KernelArgumentBlock& args,
- ElfW(Addr) linker_addr,
- soinfo& linker_so);
+__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& linker_so);
/*
* This is the entry point for the linker, called from begin.S. This
@@ -543,18 +517,18 @@
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
- soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
+ soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0);
- linker_so.base = linker_addr;
- linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
- linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
- linker_so.dynamic = nullptr;
- linker_so.phdr = phdr;
- linker_so.phnum = elf_hdr->e_phnum;
- linker_so.set_linker_flag();
+ tmp_linker_so.base = linker_addr;
+ tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
+ tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
+ tmp_linker_so.dynamic = nullptr;
+ tmp_linker_so.phdr = phdr;
+ tmp_linker_so.phnum = elf_hdr->e_phnum;
+ tmp_linker_so.set_linker_flag();
// Prelink the linker so we can access linker globals.
- if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
+ if (!tmp_linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
// This might not be obvious... The reasons why we pass g_empty_list
// in place of local_group here are (1) we do not really need it, because
@@ -562,9 +536,9 @@
// itself without having to look into local_group and (2) allocators
// are not yet initialized, and therefore we cannot use linked_list.push_*
// functions at this point.
- if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
+ if (!tmp_linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
- return __linker_init_post_relocation(args, linker_addr, linker_so);
+ return __linker_init_post_relocation(args, tmp_linker_so);
}
/*
@@ -574,15 +548,13 @@
* function, so avoid inlining this function (http://b/80503879).
*/
static ElfW(Addr) __attribute__((noinline))
-__linker_init_post_relocation(KernelArgumentBlock& args,
- ElfW(Addr) linker_addr,
- soinfo& linker_so) {
+__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) {
// Initialize the main thread (including TLS, so system calls really work).
__libc_init_main_thread(args);
// We didn't protect the linker's RELRO pages in link_image because we
// couldn't make system calls on x86 at that point, but we can now...
- if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
+ if (!tmp_linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
// Initialize the linker/libc.so shared global inside the linker.
static libc_shared_globals shared_globals;
@@ -599,7 +571,7 @@
g_envp = args.envp;
// Initialize the linker's own global variables
- linker_so.call_constructors();
+ tmp_linker_so.call_constructors();
// If the linker is not acting as PT_INTERP entry_point is equal to
// _start. Which means that the linker is running as an executable and
@@ -615,13 +587,12 @@
exit(0);
}
- init_linker_info_for_gdb(linker_addr, kLinkerPath);
-
// Initialize static variables. Note that in order to
// get correct libdl_info we need to call constructors
// before get_libdl_info().
- sonext = solist = get_libdl_info(kLinkerPath, linker_so, linker_link_map);
- g_default_namespace.add_soinfo(solist);
+ sonext = solist = solinker = get_libdl_info(kLinkerPath, tmp_linker_so);
+ g_default_namespace.add_soinfo(solinker);
+ init_link_map_head(*solinker, kLinkerPath);
args.abort_message_ptr = &g_abort_message;
ElfW(Addr) start_address = linker_main(args);
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 54354a8..0a7ccd8 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -31,6 +31,7 @@
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -41,7 +42,6 @@
#include "linker_debug.h"
#include "linker_utils.h"
-#include "private/bionic_prctl.h"
#include "private/CFIShadow.h" // For kLibraryAlignment
static int GetTargetElfMachine() {
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 09e5065..7331b2f 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -361,9 +361,7 @@
android_namespace_list_t secondary_namespaces_;
uintptr_t handle_;
- friend soinfo* get_libdl_info(const char* linker_path,
- const soinfo& linker_si,
- const link_map& linker_map);
+ friend soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si);
// version >= 4
ElfW(Relr)* relr_;
diff --git a/tests/bug_26110743_test.cpp b/tests/bug_26110743_test.cpp
index 89c6dcc..883280f 100644
--- a/tests/bug_26110743_test.cpp
+++ b/tests/bug_26110743_test.cpp
@@ -22,6 +22,8 @@
#include <sys/stat.h>
#include <sys/prctl.h>
+#include <string>
+
#include <android-base/scopeguard.h>
extern "C" pid_t gettid();
@@ -35,8 +37,9 @@
const char* ERRORMSG = "Please apply the following two kernel patches:\n"
"* https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=73af963f9f3036dffed55c3a2898598186db1045\n"
"* https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=96d0df79f2644fc823f26c06491e182d87a90c2a\n";
- ASSERT_NE(-1, readlink(buf, buf2, sizeof(buf2))) << ERRORMSG;
- ASSERT_STREQ("/dev/null", buf2);
+ ssize_t length = readlink(buf, buf2, sizeof(buf2));
+ ASSERT_LT(0, length) << ERRORMSG;
+ ASSERT_EQ("/dev/null", std::string(buf2, length));
close(fd);
}
@@ -79,8 +82,9 @@
snprintf(buf, sizeof(buf), "/proc/%d/task/%d/fd/%d", mypid, mytid, fd);
const char* ERRORMSG = "Please apply the following kernel patch:\n"
"* https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=54708d2858e79a2bdda10bf8a20c80eb96c20613\n";
- ASSERT_NE(-1, readlink(buf, buf2, sizeof(buf2))) << ERRORMSG;
- ASSERT_STREQ("/dev/null", buf2);
+ ssize_t length = readlink(buf, buf2, sizeof(buf2));
+ ASSERT_LT(0, length) << ERRORMSG;
+ ASSERT_EQ("/dev/null", std::string(buf2, length));
close(fd);
}
diff --git a/tests/link_test.cpp b/tests/link_test.cpp
index a5430a9..1bdee9f 100644
--- a/tests/link_test.cpp
+++ b/tests/link_test.cpp
@@ -29,6 +29,12 @@
#include <gtest/gtest.h>
#include <link.h>
+#if __has_include(<sys/auxv.h>)
+#include <sys/auxv.h>
+#endif
+
+#include <string>
+#include <unordered_map>
TEST(link, dl_iterate_phdr_early_exit) {
static size_t call_count = 0;
@@ -40,7 +46,7 @@
TEST(link, dl_iterate_phdr) {
struct Functor {
static int Callback(dl_phdr_info* i, size_t s, void* data) {
- reinterpret_cast<Functor*>(data)->DoChecks(i, s);
+ static_cast<Functor*>(data)->DoChecks(i, s);
return 0;
}
void DoChecks(dl_phdr_info* info, size_t s) {
@@ -48,7 +54,12 @@
ASSERT_TRUE(info->dlpi_name != nullptr);
+ // An ELF file must have at least a PT_LOAD program header.
+ ASSERT_NE(nullptr, info->dlpi_phdr);
+ ASSERT_NE(0, info->dlpi_phnum);
+
// Find the first PT_LOAD program header so we can find the ELF header.
+ bool found_load = false;
for (ElfW(Half) i = 0; i < info->dlpi_phnum; ++i) {
const ElfW(Phdr)* phdr = reinterpret_cast<const ElfW(Phdr)*>(&info->dlpi_phdr[i]);
if (phdr->p_type == PT_LOAD) {
@@ -58,15 +69,124 @@
ASSERT_EQ(0, memcmp(ehdr, ELFMAG, SELFMAG));
// Does the e_phnum match what dl_iterate_phdr told us?
ASSERT_EQ(info->dlpi_phnum, ehdr->e_phnum);
+ found_load = true;
break;
}
}
+ ASSERT_EQ(true, found_load);
}
size_t count;
} f = {};
ASSERT_EQ(0, dl_iterate_phdr(Functor::Callback, &f));
}
+struct ProgHdr {
+ const ElfW(Phdr)* table;
+ size_t size;
+};
+
+__attribute__((__unused__))
+static ElfW(Addr) find_exe_load_bias(const ProgHdr& phdr) {
+ for (size_t i = 0; i < phdr.size; ++i) {
+ if (phdr.table[i].p_type == PT_PHDR) {
+ return reinterpret_cast<ElfW(Addr)>(phdr.table) - phdr.table[i].p_vaddr;
+ }
+ }
+ return 0;
+}
+
+__attribute__((__unused__))
+static ElfW(Dyn)* find_dynamic(const ProgHdr& phdr, ElfW(Addr) load_bias) {
+ for (size_t i = 0; i < phdr.size; ++i) {
+ if (phdr.table[i].p_type == PT_DYNAMIC) {
+ return reinterpret_cast<ElfW(Dyn)*>(phdr.table[i].p_vaddr + load_bias);
+ }
+ }
+ return nullptr;
+}
+
+__attribute__((__unused__))
+static r_debug* find_exe_r_debug(ElfW(Dyn)* dynamic) {
+ for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
+ if (d->d_tag == DT_DEBUG) {
+ return reinterpret_cast<r_debug*>(d->d_un.d_val);
+ }
+ }
+ return nullptr;
+}
+
+// Walk the DT_DEBUG/_r_debug global module list and compare it with the same
+// information from dl_iterate_phdr. Verify that the executable appears first
+// in _r_debug.
+TEST(link, r_debug) {
+#if __has_include(<sys/auxv.h>)
+ // Find the executable's PT_DYNAMIC segment and DT_DEBUG value. The linker
+ // will write the address of its _r_debug global into the .dynamic section.
+ ProgHdr exe_phdr = {
+ .table = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)),
+ .size = getauxval(AT_PHNUM)
+ };
+ ASSERT_NE(nullptr, exe_phdr.table);
+ ElfW(Addr) exe_load_bias = find_exe_load_bias(exe_phdr);
+ ASSERT_NE(0u, exe_load_bias);
+ ElfW(Dyn)* exe_dynamic = find_dynamic(exe_phdr, exe_load_bias);
+ ASSERT_NE(nullptr, exe_dynamic);
+ r_debug* dbg = find_exe_r_debug(exe_dynamic);
+ ASSERT_NE(nullptr, dbg);
+
+ // Use dl_iterate_phdr to build a table mapping from load bias values to
+ // solib names and PT_DYNAMIC segments.
+ struct DlIterateInfo {
+ std::string name;
+ ElfW(Dyn)* dynamic;
+ };
+ struct Functor {
+ std::unordered_map<ElfW(Addr), DlIterateInfo> dl_iter_mods;
+ static int Callback(dl_phdr_info* i, size_t s, void* data) {
+ static_cast<Functor*>(data)->AddModule(i, s);
+ return 0;
+ }
+ void AddModule(dl_phdr_info* info, size_t s) {
+ ASSERT_EQ(sizeof(dl_phdr_info), s);
+ ASSERT_TRUE(dl_iter_mods.find(info->dlpi_addr) == dl_iter_mods.end());
+ ASSERT_TRUE(info->dlpi_name != nullptr);
+ dl_iter_mods[info->dlpi_addr] = {
+ .name = info->dlpi_name,
+ .dynamic = find_dynamic({ info->dlpi_phdr, info->dlpi_phnum }, info->dlpi_addr)
+ };
+ }
+ } f = {};
+ ASSERT_EQ(0, dl_iterate_phdr(Functor::Callback, &f));
+
+ size_t map_size = 0;
+
+ for (link_map* map = dbg->r_map; map != nullptr; map = map->l_next) {
+ ASSERT_NE(0u, map->l_addr);
+ ASSERT_NE(nullptr, map->l_ld);
+ ASSERT_NE(nullptr, map->l_name);
+
+ auto it = f.dl_iter_mods.find(map->l_addr);
+ ASSERT_TRUE(it != f.dl_iter_mods.end());
+ const DlIterateInfo& info = it->second;
+ ASSERT_EQ(info.name, map->l_name);
+ ASSERT_EQ(info.dynamic, map->l_ld);
+
+ ++map_size;
+ }
+
+ // _r_debug and dl_iterate_phdr should report the same set of modules. We
+ // verified above that every _r_debug module was reported by dl_iterate_phdr,
+ // so checking the sizes verifies the converse.
+ ASSERT_EQ(f.dl_iter_mods.size(), map_size);
+
+ // Make sure the first entry is the executable. gdbserver assumes this and
+ // removes the first entry from its list of shared objects that it sends back
+ // to gdb.
+ ASSERT_EQ(exe_load_bias, dbg->r_map->l_addr);
+ ASSERT_EQ(exe_dynamic, dbg->r_map->l_ld);
+#endif
+}
+
#if __arm__
static uintptr_t read_exidx_func(uintptr_t* entry) {
int32_t offset = *entry;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 844a9c0..d96da02 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -2506,3 +2506,19 @@
fclose(fp);
}
+
+TEST(STDIO_TEST, dev_std_files) {
+ // POSIX only mentions /dev/stdout, but we should have all three (http://b/31824379).
+ char path[PATH_MAX];
+ ssize_t length = readlink("/dev/stdin", path, sizeof(path));
+ ASSERT_LT(0, length);
+ ASSERT_EQ("/proc/self/fd/0", std::string(path, length));
+
+ length = readlink("/dev/stdout", path, sizeof(path));
+ ASSERT_LT(0, length);
+ ASSERT_EQ("/proc/self/fd/1", std::string(path, length));
+
+ length = readlink("/dev/stderr", path, sizeof(path));
+ ASSERT_LT(0, length);
+ ASSERT_EQ("/proc/self/fd/2", std::string(path, length));
+}
diff --git a/tests/sys_prctl_test.cpp b/tests/sys_prctl_test.cpp
index 2123c94..7afa626 100644
--- a/tests/sys_prctl_test.cpp
+++ b/tests/sys_prctl_test.cpp
@@ -29,11 +29,10 @@
#include "android-base/file.h"
#include "android-base/strings.h"
-#include "private/bionic_prctl.h"
// http://b/20017123.
TEST(sys_prctl, bug_20017123) {
-#if defined(__ANDROID__) // PR_SET_VMA is only available in Android kernels.
+#if defined(PR_SET_VMA) // PR_SET_VMA is only available in Android kernels.
size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
void* p = mmap(NULL, page_size * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(MAP_FAILED, p);