Merge changes from topic "move_bionic_to_apex"
* changes:
Expose more symbols temporarily
Add stubs variants for bionic libs
diff --git a/libc/Android.bp b/libc/Android.bp
index 4f371af..e74060c 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -760,6 +760,7 @@
"arch-arm/generic/bionic/strcpy.S",
"arch-arm/generic/bionic/strlen.c",
+ "arch-arm/bionic/__aeabi_read_tp.S",
"arch-arm/bionic/atomics_arm.c",
"arch-arm/bionic/__bionic_clone.S",
"arch-arm/bionic/_exit_with_stack_teardown.S",
diff --git a/libc/arch-arm/bionic/__aeabi_read_tp.S b/libc/arch-arm/bionic/__aeabi_read_tp.S
new file mode 100644
index 0000000..f6f9757
--- /dev/null
+++ b/libc/arch-arm/bionic/__aeabi_read_tp.S
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 <private/bionic_asm.h>
+
+// For arm32 and -mtp=soft, the compiler uses this function to read the thread
+// pointer for ELF TLS accesses. With -mtp=cp15, the compiler inlines the call
+// instead. GCC defaults to -mtp=auto, which inlines this function with
+// -march=armv7-a. Clang does not yet implement -mtp=auto, and instead defaults
+// to -mtp=soft.
+// - https://bugs.llvm.org/show_bug.cgi?id=38394.
+// - https://reviews.llvm.org/D34878?id=114573.
+//
+// This function must preserve every register except r0, ip, lr, and cpsr.
+//
+// See "Run-time ABI for the ARM Architecture"
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0043d/IHI0043D_rtabi.pdf
+//
+ENTRY(__aeabi_read_tp)
+ // __get_tls()
+ mrc p15, 0, r0, c13, c0, 3
+ bx lr
+END(__aeabi_read_tp)
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 8e8d180..06d2ecb 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -76,8 +76,6 @@
return nullptr;
}
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, allocation, allocation_size, "bionic TLS guard");
-
// Carve out the writable TLS section.
bionic_tls* result = reinterpret_cast<bionic_tls*>(static_cast<char*>(allocation) +
PTHREAD_GUARD_SIZE);
@@ -88,7 +86,6 @@
return nullptr;
}
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, result, BIONIC_TLS_SIZE, "bionic TLS");
return result;
}
@@ -111,7 +108,6 @@
// We can only use const static allocated string for mapped region name, as Android kernel
// uses the string pointer directly when dumping /proc/pid/maps.
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ss.ss_sp, ss.ss_size, "thread signal stack");
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, stack_base, PTHREAD_GUARD_SIZE, "thread signal stack guard");
}
}
@@ -214,8 +210,6 @@
munmap(space, mmap_size);
return nullptr;
}
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, space, stack_guard_size, "thread stack guard");
-
return space;
}
diff --git a/libc/kernel/tools/cpp.py b/libc/kernel/tools/cpp.py
index 336a9c8..1ada59e 100755
--- a/libc/kernel/tools/cpp.py
+++ b/libc/kernel/tools/cpp.py
@@ -1037,11 +1037,14 @@
if t.id == '{':
buf += ' {'
result.append(strip_space(buf))
- indent += 2
+ # Do not indent if this is extern "C" {
+ if i < 2 or tokens[i-2].id != 'extern' or tokens[i-1].id != '"C"':
+ indent += 2
buf = ''
newline = True
elif t.id == '}':
- indent -= 2
+ if indent >= 2:
+ indent -= 2
if not newline:
result.append(strip_space(buf))
# Look ahead to determine if it's the end of line.
@@ -1221,133 +1224,140 @@
function declarations are removed. We only accept typedefs and
enum/structs/union declarations.
+ In addition, remove any macros expanding in the headers. Usually,
+ these macros are static inline functions, which is why they are
+ removed.
+
However, we keep the definitions corresponding to the set of known
static inline functions in the set 'keep', which is useful
for optimized byteorder swap functions and stuff like that.
"""
- # NOTE: It's also removing function-like macros, such as __SYSCALL(...)
- # in uapi/asm-generic/unistd.h, or KEY_FIELD(...) in linux/bcache.h.
- # It could be problematic when we have function-like macros but without
- # '}' following them. It will skip all the tokens/blocks until seeing a
- # '}' as the function end. Fortunately we don't have such cases in the
- # current kernel headers.
+ # state = NORMAL => normal (i.e. LN + spaces)
+ # state = OTHER_DECL => typedef/struct encountered, ends with ";"
+ # state = VAR_DECL => var declaration encountered, ends with ";"
+ # state = FUNC_DECL => func declaration encountered, ends with "}"
+ NORMAL = 0
+ OTHER_DECL = 1
+ VAR_DECL = 2
+ FUNC_DECL = 3
- # state = 0 => normal (i.e. LN + spaces)
- # state = 1 => typedef/struct encountered, ends with ";"
- # state = 2 => var declaration encountered, ends with ";"
- # state = 3 => func declaration encountered, ends with "}"
-
- state = 0
+ state = NORMAL
depth = 0
- blocks2 = []
- skipTokens = False
- for b in self.blocks:
- if b.isDirective():
- blocks2.append(b)
- else:
- n = len(b.tokens)
- i = 0
- if skipTokens:
- first = n
- else:
- first = 0
- while i < n:
- tok = b.tokens[i]
- tokid = tok.id
- # If we are not looking for the start of a new
- # type/var/func, then skip over tokens until
- # we find our terminator, managing the depth of
- # accolades as we go.
- if state > 0:
- terminator = False
- if tokid == '{':
- depth += 1
- elif tokid == '}':
- if depth > 0:
- depth -= 1
- if (depth == 0) and (state == 3):
- terminator = True
- elif tokid == ';' and depth == 0:
- terminator = True
-
- if terminator:
- # we found the terminator
- state = 0
- if skipTokens:
- skipTokens = False
- first = i + 1
-
- i += 1
- continue
-
- # Is it a new type definition, then start recording it
- if tok.id in ['struct', 'typedef', 'enum', 'union',
- '__extension__']:
- state = 1
- i += 1
- continue
-
- # Is it a variable or function definition. If so, first
- # try to determine which type it is, and also extract
- # its name.
- #
- # We're going to parse the next tokens of the same block
- # until we find a semicolon or a left parenthesis.
- #
- # The semicolon corresponds to a variable definition,
- # the left-parenthesis to a function definition.
- #
- # We also assume that the var/func name is the last
- # identifier before the terminator.
- #
- j = i + 1
- ident = ""
- while j < n:
- tokid = b.tokens[j].id
- if tokid == '(': # a function declaration
- state = 3
- break
- elif tokid == ';': # a variable declaration
- state = 2
- break
- if b.tokens[j].kind == TokenKind.IDENTIFIER:
- ident = b.tokens[j].id
- j += 1
-
- if j >= n:
- # This can only happen when the declaration
- # does not end on the current block (e.g. with
- # a directive mixed inside it.
- #
- # We will treat it as malformed because
- # it's very hard to recover from this case
- # without making our parser much more
- # complex.
- #
- logging.debug("### skip unterminated static '%s'",
- ident)
- break
-
- if ident in keep:
- logging.debug("### keep var/func '%s': %s", ident,
- repr(b.tokens[i:j]))
+ blocksToKeep = []
+ blocksInProgress = []
+ blocksOfDirectives = []
+ ident = ""
+ state_token = ""
+ macros = set()
+ for block in self.blocks:
+ if block.isDirective():
+ # Record all macros.
+ if block.directive == 'define':
+ macro_name = block.define_id
+ paren_index = macro_name.find('(')
+ if paren_index == -1:
+ macros.add(macro_name)
else:
- # We're going to skip the tokens for this declaration
- logging.debug("### skip var/func '%s': %s", ident,
- repr(b.tokens[i:j]))
- if i > first:
- blocks2.append(Block(b.tokens[first:i]))
- skipTokens = True
- first = n
+ macros.add(macro_name[0:paren_index])
+ blocksInProgress.append(block)
+ # If this is in a function/variable declaration, we might need
+ # to emit the directives alone, so save them separately.
+ blocksOfDirectives.append(block)
+ continue
- i += 1
+ numTokens = len(block.tokens)
+ lastTerminatorIndex = 0
+ i = 0
+ while i < numTokens:
+ token_id = block.tokens[i].id
+ terminator = False
+ if token_id == '{':
+ depth += 1
+ if (i >= 2 and block.tokens[i-2].id == 'extern' and
+ block.tokens[i-1].id == '"C"'):
+ # For an extern "C" { pretend as though this is depth 0.
+ depth -= 1
+ elif token_id == '}':
+ if depth > 0:
+ depth -= 1
+ if depth == 0:
+ if state == OTHER_DECL:
+ # Loop through until we hit the ';'
+ i += 1
+ while i < numTokens:
+ if block.tokens[i].id == ';':
+ token_id = ';'
+ break
+ i += 1
+ # If we didn't hit the ';', just consider this the
+ # terminator any way.
+ terminator = True
+ elif depth == 0:
+ if token_id == ';':
+ if state == NORMAL:
+ blocksToKeep.extend(blocksInProgress)
+ blocksInProgress = []
+ blocksOfDirectives = []
+ state = FUNC_DECL
+ terminator = True
+ elif (state == NORMAL and token_id == '(' and i >= 1 and
+ block.tokens[i-1].kind == TokenKind.IDENTIFIER and
+ block.tokens[i-1].id in macros):
+ # This is a plain macro being expanded in the header
+ # which needs to be removed.
+ blocksToKeep.extend(blocksInProgress)
+ if lastTerminatorIndex < i - 1:
+ blocksToKeep.append(Block(block.tokens[lastTerminatorIndex:i-1]))
+ blocksInProgress = []
+ blocksOfDirectives = []
- if i > first:
- #print "### final '%s'" % repr(b.tokens[first:i])
- blocks2.append(Block(b.tokens[first:i]))
+ # Skip until we see the terminating ')'
+ i += 1
+ paren_depth = 1
+ while i < numTokens:
+ if block.tokens[i].id == ')':
+ paren_depth -= 1
+ if paren_depth == 0:
+ break
+ elif block.tokens[i].id == '(':
+ paren_depth += 1
+ i += 1
+ lastTerminatorIndex = i + 1
+ elif (state != FUNC_DECL and token_id == '(' and
+ state_token != 'typedef'):
+ blocksToKeep.extend(blocksInProgress)
+ blocksInProgress = []
+ blocksOfDirectives = []
+ state = VAR_DECL
+ elif state == NORMAL and token_id in ['struct', 'typedef',
+ 'enum', 'union',
+ '__extension__']:
+ state = OTHER_DECL
+ state_token = token_id
+ elif block.tokens[i].kind == TokenKind.IDENTIFIER:
+ if state != VAR_DECL or ident == "":
+ ident = token_id
- self.blocks = blocks2
+ if terminator:
+ if state != VAR_DECL and state != FUNC_DECL or ident in keep:
+ blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:i+1]))
+ blocksToKeep.extend(blocksInProgress)
+ else:
+ # Only keep the directives found.
+ blocksToKeep.extend(blocksOfDirectives)
+ lastTerminatorIndex = i + 1
+ blocksInProgress = []
+ blocksOfDirectives = []
+ state = NORMAL
+ ident = ""
+ state_token = ""
+ i += 1
+ if lastTerminatorIndex < numTokens:
+ blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:numTokens]))
+ if len(blocksInProgress) > 0:
+ blocksToKeep.extend(blocksInProgress)
+ self.blocks = blocksToKeep
def replaceTokens(self, replacements):
"""Replace tokens according to the given dict."""
@@ -1938,6 +1948,299 @@
expected = ""
self.assertEqual(self.parse(text), expected)
+class FullPathTest(unittest.TestCase):
+ """Test of the full path parsing."""
+
+ def parse(self, text, keep=None):
+ if not keep:
+ keep = set()
+ out = utils.StringOutput()
+ blocks = BlockParser().parse(CppStringTokenizer(text))
+ blocks.removeVarsAndFuncs(keep)
+ blocks.replaceTokens(kernel_token_replacements)
+ blocks.optimizeAll(None)
+ blocks.write(out)
+ return out.get()
+
+ def test_function_removed(self):
+ text = """\
+static inline __u64 function()
+{
+}
+"""
+ expected = ""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_function_removed_with_struct(self):
+ text = """\
+static inline struct something* function()
+{
+}
+"""
+ expected = ""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_function_kept(self):
+ text = """\
+static inline __u64 function()
+{
+}
+"""
+ expected = """\
+static inline __u64 function() {
+}
+"""
+ self.assertEqual(self.parse(text, set(["function"])), expected)
+
+ def test_var_removed(self):
+ text = "__u64 variable;"
+ expected = ""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_var_kept(self):
+ text = "__u64 variable;"
+ expected = "__u64 variable;\n"
+ self.assertEqual(self.parse(text, set(["variable"])), expected)
+
+ def test_keep_function_typedef(self):
+ text = "typedef void somefunction_t(void);"
+ expected = "typedef void somefunction_t(void);\n"
+ self.assertEqual(self.parse(text), expected)
+
+ def test_struct_keep_attribute(self):
+ text = """\
+struct something_s {
+ __u32 s1;
+ __u32 s2;
+} __attribute__((packed));
+"""
+ expected = """\
+struct something_s {
+ __u32 s1;
+ __u32 s2;
+} __attribute__((packed));
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_function_keep_attribute_structs(self):
+ text = """\
+static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
+}
+"""
+ expected = """\
+static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
+}
+"""
+ self.assertEqual(self.parse(text, set(["function"])), expected)
+
+ def test_struct_after_struct(self):
+ text = """\
+struct first {
+};
+
+struct second {
+ unsigned short s1;
+#define SOMETHING 8
+ unsigned short s2;
+};
+"""
+ expected = """\
+struct first {
+};
+struct second {
+ unsigned short s1;
+#define SOMETHING 8
+ unsigned short s2;
+};
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_other_not_removed(self):
+ text = """\
+typedef union {
+ __u64 tu1;
+ __u64 tu2;
+} typedef_name;
+
+union {
+ __u64 u1;
+ __u64 u2;
+};
+
+struct {
+ __u64 s1;
+ __u64 s2;
+};
+
+enum {
+ ENUM1 = 0,
+ ENUM2,
+};
+
+__extension__ typedef __signed__ long long __s64;
+"""
+ expected = """\
+typedef union {
+ __u64 tu1;
+ __u64 tu2;
+} typedef_name;
+union {
+ __u64 u1;
+ __u64 u2;
+};
+struct {
+ __u64 s1;
+ __u64 s2;
+};
+enum {
+ ENUM1 = 0,
+ ENUM2,
+};
+__extension__ typedef __signed__ long long __s64;
+"""
+
+ self.assertEqual(self.parse(text), expected)
+
+ def test_semicolon_after_function(self):
+ text = """\
+static inline __u64 function()
+{
+};
+
+struct should_see {
+ __u32 field;
+};
+"""
+ expected = """\
+struct should_see {
+ __u32 field;
+};
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_define_in_middle_keep(self):
+ text = """\
+enum {
+ ENUM0 = 0x10,
+ ENUM1 = 0x20,
+#define SOMETHING SOMETHING_ELSE
+ ENUM2 = 0x40,
+};
+"""
+ expected = """\
+enum {
+ ENUM0 = 0x10,
+ ENUM1 = 0x20,
+#define SOMETHING SOMETHING_ELSE
+ ENUM2 = 0x40,
+};
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_define_in_middle_remove(self):
+ text = """\
+static inline function() {
+#define SOMETHING1 SOMETHING_ELSE1
+ i = 0;
+ {
+ i = 1;
+ }
+#define SOMETHING2 SOMETHING_ELSE2
+}
+"""
+ expected = """\
+#define SOMETHING1 SOMETHING_ELSE1
+#define SOMETHING2 SOMETHING_ELSE2
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_define_in_middle_force_keep(self):
+ text = """\
+static inline function() {
+#define SOMETHING1 SOMETHING_ELSE1
+ i = 0;
+ {
+ i = 1;
+ }
+#define SOMETHING2 SOMETHING_ELSE2
+}
+"""
+ expected = """\
+static inline function() {
+#define SOMETHING1 SOMETHING_ELSE1
+ i = 0;
+ {
+ i = 1;
+ }
+#define SOMETHING2 SOMETHING_ELSE2
+}
+"""
+ self.assertEqual(self.parse(text, set(["function"])), expected)
+
+ def test_define_before_remove(self):
+ text = """\
+#define SHOULD_BE_KEPT NOTHING1
+#define ANOTHER_TO_KEEP NOTHING2
+static inline function() {
+#define SOMETHING1 SOMETHING_ELSE1
+ i = 0;
+ {
+ i = 1;
+ }
+#define SOMETHING2 SOMETHING_ELSE2
+}
+"""
+ expected = """\
+#define SHOULD_BE_KEPT NOTHING1
+#define ANOTHER_TO_KEEP NOTHING2
+#define SOMETHING1 SOMETHING_ELSE1
+#define SOMETHING2 SOMETHING_ELSE2
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_extern_C(self):
+ text = """\
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct something {
+};
+
+#if defined(__cplusplus)
+}
+#endif
+"""
+ expected = """\
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct something {
+};
+#ifdef __cplusplus
+}
+#endif
+"""
+ self.assertEqual(self.parse(text), expected)
+
+ def test_macro_definition_removed(self):
+ text = """\
+#define MACRO_FUNCTION_NO_PARAMS static inline some_func() {}
+MACRO_FUNCTION_NO_PARAMS()
+
+#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
+MACRO_FUNCTION_PARAMS(a = 1)
+
+something that should still be kept
+MACRO_FUNCTION_PARAMS(b)
+"""
+ expected = """\
+#define MACRO_FUNCTION_NO_PARAMS static inline some_func() { }
+#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
+something that should still be kept
+"""
+ self.assertEqual(self.parse(text), expected)
+
if __name__ == '__main__':
unittest.main()
diff --git a/libc/kernel/uapi/drm/amdgpu_drm.h b/libc/kernel/uapi/drm/amdgpu_drm.h
index bdf59d6..2013fa7 100644
--- a/libc/kernel/uapi/drm/amdgpu_drm.h
+++ b/libc/kernel/uapi/drm/amdgpu_drm.h
@@ -20,6 +20,7 @@
#define __AMDGPU_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_AMDGPU_GEM_CREATE 0x00
#define DRM_AMDGPU_GEM_MMAP 0x01
@@ -593,5 +594,6 @@
#define AMDGPU_FAMILY_AI 141
#define AMDGPU_FAMILY_RV 142
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/armada_drm.h b/libc/kernel/uapi/drm/armada_drm.h
index ea1f37d..aabd23b 100644
--- a/libc/kernel/uapi/drm/armada_drm.h
+++ b/libc/kernel/uapi/drm/armada_drm.h
@@ -20,6 +20,7 @@
#define DRM_ARMADA_IOCTL_H
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_ARMADA_GEM_CREATE 0x00
#define DRM_ARMADA_GEM_MMAP 0x02
@@ -46,5 +47,6 @@
};
#define DRM_IOCTL_ARMADA_GEM_PWRITE ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/drm.h b/libc/kernel/uapi/drm/drm.h
index 21f23f4..ef64ed7 100644
--- a/libc/kernel/uapi/drm/drm.h
+++ b/libc/kernel/uapi/drm/drm.h
@@ -37,6 +37,7 @@
typedef unsigned long drm_handle_t;
#endif
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_NAME "drm"
#define DRM_MIN_ORDER 5
@@ -429,9 +430,11 @@
__u64 user_data;
};
#ifdef __cplusplus
+}
#endif
#include "drm_mode.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_IOCTL_BASE 'd'
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE, nr)
@@ -603,5 +606,6 @@
typedef struct drm_scatter_gather drm_scatter_gather_t;
typedef struct drm_set_version drm_set_version_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/drm_fourcc.h b/libc/kernel/uapi/drm/drm_fourcc.h
index 9634e99..4589cfe 100644
--- a/libc/kernel/uapi/drm/drm_fourcc.h
+++ b/libc/kernel/uapi/drm/drm_fourcc.h
@@ -20,6 +20,7 @@
#define DRM_FOURCC_H
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define fourcc_code(a,b,c,d) ((__u32) (a) | ((__u32) (b) << 8) | ((__u32) (c) << 16) | ((__u32) (d) << 24))
#define DRM_FORMAT_BIG_ENDIAN (1 << 31)
@@ -156,5 +157,6 @@
#define AFBC_FORMAT_MOD_TILED (1ULL << 8)
#define AFBC_FORMAT_MOD_SC (1ULL << 9)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/drm_mode.h b/libc/kernel/uapi/drm/drm_mode.h
index 06c26e4..dff9f34 100644
--- a/libc/kernel/uapi/drm/drm_mode.h
+++ b/libc/kernel/uapi/drm/drm_mode.h
@@ -20,6 +20,7 @@
#define _DRM_MODE_H
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_DISPLAY_INFO_LEN 32
#define DRM_CONNECTOR_NAME_LEN 32
@@ -463,5 +464,6 @@
__u32 lessee_id;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/drm_sarea.h b/libc/kernel/uapi/drm/drm_sarea.h
index 03317b9..a0c7f3a 100644
--- a/libc/kernel/uapi/drm/drm_sarea.h
+++ b/libc/kernel/uapi/drm/drm_sarea.h
@@ -20,6 +20,7 @@
#define _DRM_SAREA_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifdef __alpha__
#define SAREA_MAX 0x2000U
@@ -54,5 +55,6 @@
typedef struct drm_sarea_frame drm_sarea_frame_t;
typedef struct drm_sarea drm_sarea_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/etnaviv_drm.h b/libc/kernel/uapi/drm/etnaviv_drm.h
index bb502d9..4c09e6c 100644
--- a/libc/kernel/uapi/drm/etnaviv_drm.h
+++ b/libc/kernel/uapi/drm/etnaviv_drm.h
@@ -20,6 +20,7 @@
#define __ETNAVIV_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
struct drm_etnaviv_timespec {
__s64 tv_sec;
@@ -193,5 +194,6 @@
#define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain)
#define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/exynos_drm.h b/libc/kernel/uapi/drm/exynos_drm.h
index 8b27cbb..4918035 100644
--- a/libc/kernel/uapi/drm/exynos_drm.h
+++ b/libc/kernel/uapi/drm/exynos_drm.h
@@ -20,6 +20,7 @@
#define _UAPI_EXYNOS_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
struct drm_exynos_gem_create {
__u64 size;
@@ -225,5 +226,6 @@
__u64 reserved;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/i810_drm.h b/libc/kernel/uapi/drm/i810_drm.h
index 1fd3c46..e33387d 100644
--- a/libc/kernel/uapi/drm/i810_drm.h
+++ b/libc/kernel/uapi/drm/i810_drm.h
@@ -20,6 +20,7 @@
#define _I810_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef _I810_DEFINES_
#define _I810_DEFINES_
@@ -214,5 +215,6 @@
unsigned int last_render;
} drm_i810_mc_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/i915_drm.h b/libc/kernel/uapi/drm/i915_drm.h
index 4c1d87f..53d1548 100644
--- a/libc/kernel/uapi/drm/i915_drm.h
+++ b/libc/kernel/uapi/drm/i915_drm.h
@@ -20,6 +20,7 @@
#define _UAPI_I915_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR"
#define I915_ERROR_UEVENT "ERROR"
@@ -775,5 +776,6 @@
__u8 data[];
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/mga_drm.h b/libc/kernel/uapi/drm/mga_drm.h
index 4d5ad0c..4959502 100644
--- a/libc/kernel/uapi/drm/mga_drm.h
+++ b/libc/kernel/uapi/drm/mga_drm.h
@@ -20,6 +20,7 @@
#define __MGA_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef __MGA_SAREA_DEFINES__
#define __MGA_SAREA_DEFINES__
@@ -240,5 +241,6 @@
void __user * value;
} drm_mga_getparam_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/msm_drm.h b/libc/kernel/uapi/drm/msm_drm.h
index 1d53c5d..df8119f 100644
--- a/libc/kernel/uapi/drm/msm_drm.h
+++ b/libc/kernel/uapi/drm/msm_drm.h
@@ -20,6 +20,7 @@
#define __MSM_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define MSM_PIPE_NONE 0x00
#define MSM_PIPE_2D0 0x01
@@ -158,5 +159,6 @@
#define DRM_IOCTL_MSM_SUBMITQUEUE_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, struct drm_msm_submitqueue)
#define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, __u32)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/nouveau_drm.h b/libc/kernel/uapi/drm/nouveau_drm.h
index f9c3447..54c3b97 100644
--- a/libc/kernel/uapi/drm/nouveau_drm.h
+++ b/libc/kernel/uapi/drm/nouveau_drm.h
@@ -21,6 +21,7 @@
#define DRM_NOUVEAU_EVENT_NVIF 0x80000000
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0)
#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1)
@@ -122,5 +123,6 @@
#define DRM_IOCTL_NOUVEAU_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_FINI, struct drm_nouveau_gem_cpu_fini)
#define DRM_IOCTL_NOUVEAU_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_INFO, struct drm_nouveau_gem_info)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/omap_drm.h b/libc/kernel/uapi/drm/omap_drm.h
index 54b539a..3c2fc08 100644
--- a/libc/kernel/uapi/drm/omap_drm.h
+++ b/libc/kernel/uapi/drm/omap_drm.h
@@ -20,6 +20,7 @@
#define __OMAP_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define OMAP_PARAM_CHIPSET_ID 1
struct drm_omap_param {
@@ -84,5 +85,6 @@
#define DRM_IOCTL_OMAP_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini)
#define DRM_IOCTL_OMAP_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GEM_INFO, struct drm_omap_gem_info)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/qxl_drm.h b/libc/kernel/uapi/drm/qxl_drm.h
index 5942635..e9521c4 100644
--- a/libc/kernel/uapi/drm/qxl_drm.h
+++ b/libc/kernel/uapi/drm/qxl_drm.h
@@ -20,6 +20,7 @@
#define QXL_DRM_H
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define QXL_GEM_DOMAIN_CPU 0
#define QXL_GEM_DOMAIN_VRAM 1
@@ -97,5 +98,6 @@
#define DRM_IOCTL_QXL_CLIENTCAP DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_CLIENTCAP, struct drm_qxl_clientcap)
#define DRM_IOCTL_QXL_ALLOC_SURF DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC_SURF, struct drm_qxl_alloc_surf)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/r128_drm.h b/libc/kernel/uapi/drm/r128_drm.h
index 85dea7e..618b6dc 100644
--- a/libc/kernel/uapi/drm/r128_drm.h
+++ b/libc/kernel/uapi/drm/r128_drm.h
@@ -20,6 +20,7 @@
#define __R128_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef __R128_SAREA_DEFINES__
#define __R128_SAREA_DEFINES__
@@ -229,5 +230,6 @@
void __user * value;
} drm_r128_getparam_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/radeon_drm.h b/libc/kernel/uapi/drm/radeon_drm.h
index fe8fe67..9dc69ad 100644
--- a/libc/kernel/uapi/drm/radeon_drm.h
+++ b/libc/kernel/uapi/drm/radeon_drm.h
@@ -20,6 +20,7 @@
#define __RADEON_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef __RADEON_SAREA_DEFINES__
#define __RADEON_SAREA_DEFINES__
@@ -801,5 +802,6 @@
#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA 2
#define CIK_TILE_MODE_DEPTH_STENCIL_1D 5
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/savage_drm.h b/libc/kernel/uapi/drm/savage_drm.h
index 8c5a172..ae87d21 100644
--- a/libc/kernel/uapi/drm/savage_drm.h
+++ b/libc/kernel/uapi/drm/savage_drm.h
@@ -20,6 +20,7 @@
#define __SAVAGE_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef __SAVAGE_SAREA_DEFINES__
#define __SAVAGE_SAREA_DEFINES__
@@ -151,5 +152,6 @@
} clear1;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/sis_drm.h b/libc/kernel/uapi/drm/sis_drm.h
index ba88ea9..1606a85 100644
--- a/libc/kernel/uapi/drm/sis_drm.h
+++ b/libc/kernel/uapi/drm/sis_drm.h
@@ -20,6 +20,7 @@
#define __SIS_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define NOT_USED_0_3
#define DRM_SIS_FB_ALLOC 0x04
@@ -48,5 +49,6 @@
unsigned long offset, size;
} drm_sis_fb_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/tegra_drm.h b/libc/kernel/uapi/drm/tegra_drm.h
index ee111dc..5244a27 100644
--- a/libc/kernel/uapi/drm/tegra_drm.h
+++ b/libc/kernel/uapi/drm/tegra_drm.h
@@ -20,6 +20,7 @@
#define _UAPI_TEGRA_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0)
#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
@@ -163,5 +164,6 @@
#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags)
#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/v3d_drm.h b/libc/kernel/uapi/drm/v3d_drm.h
index c3e58cb..8865911 100644
--- a/libc/kernel/uapi/drm/v3d_drm.h
+++ b/libc/kernel/uapi/drm/v3d_drm.h
@@ -20,6 +20,7 @@
#define _V3D_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_V3D_SUBMIT_CL 0x00
#define DRM_V3D_WAIT_BO 0x01
@@ -83,5 +84,6 @@
__u32 offset;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/vc4_drm.h b/libc/kernel/uapi/drm/vc4_drm.h
index 29eb872..fde443f 100644
--- a/libc/kernel/uapi/drm/vc4_drm.h
+++ b/libc/kernel/uapi/drm/vc4_drm.h
@@ -20,6 +20,7 @@
#define _UAPI_VC4_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_VC4_SUBMIT_CL 0x00
#define DRM_VC4_WAIT_SEQNO 0x01
@@ -233,5 +234,6 @@
__u64 values_ptr;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/vgem_drm.h b/libc/kernel/uapi/drm/vgem_drm.h
index 7b0ebc9..b33452b 100644
--- a/libc/kernel/uapi/drm/vgem_drm.h
+++ b/libc/kernel/uapi/drm/vgem_drm.h
@@ -20,6 +20,7 @@
#define _UAPI_VGEM_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_VGEM_FENCE_ATTACH 0x1
#define DRM_VGEM_FENCE_SIGNAL 0x2
@@ -37,5 +38,6 @@
__u32 flags;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/via_drm.h b/libc/kernel/uapi/drm/via_drm.h
index fd4948d..9ef645a 100644
--- a/libc/kernel/uapi/drm/via_drm.h
+++ b/libc/kernel/uapi/drm/via_drm.h
@@ -20,6 +20,7 @@
#define _VIA_DRM_H_
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#ifndef _VIA_DEFINES_
#define _VIA_DEFINES_
@@ -196,5 +197,6 @@
drm_via_blitsync_t sync;
} drm_via_dmablit_t;
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/virtgpu_drm.h b/libc/kernel/uapi/drm/virtgpu_drm.h
index 6b7fb0b..84986e4 100644
--- a/libc/kernel/uapi/drm/virtgpu_drm.h
+++ b/libc/kernel/uapi/drm/virtgpu_drm.h
@@ -20,6 +20,7 @@
#define VIRTGPU_DRM_H
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_VIRTGPU_MAP 0x01
#define DRM_VIRTGPU_EXECBUFFER 0x02
@@ -113,5 +114,6 @@
#define DRM_IOCTL_VIRTGPU_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT, struct drm_virtgpu_3d_wait)
#define DRM_IOCTL_VIRTGPU_GET_CAPS DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, struct drm_virtgpu_get_caps)
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/drm/vmwgfx_drm.h b/libc/kernel/uapi/drm/vmwgfx_drm.h
index 7df722a..bb1f36d 100644
--- a/libc/kernel/uapi/drm/vmwgfx_drm.h
+++ b/libc/kernel/uapi/drm/vmwgfx_drm.h
@@ -20,6 +20,7 @@
#define __VMWGFX_DRM_H__
#include "drm.h"
#ifdef __cplusplus
+extern "C" {
#endif
#define DRM_VMW_MAX_SURFACE_FACES 6
#define DRM_VMW_MAX_MIP_LEVELS 24
@@ -350,5 +351,6 @@
struct drm_vmw_surface_arg req;
};
#ifdef __cplusplus
+}
#endif
#endif
diff --git a/libc/kernel/uapi/linux/nilfs2_api.h b/libc/kernel/uapi/linux/nilfs2_api.h
index 2f2692e..d6a6b8f 100644
--- a/libc/kernel/uapi/linux/nilfs2_api.h
+++ b/libc/kernel/uapi/linux/nilfs2_api.h
@@ -39,6 +39,16 @@
#define NILFS_CPINFO_FNS(flag,name) static inline int nilfs_cpinfo_ ##name(const struct nilfs_cpinfo * cpinfo) \
{ return ! ! (cpinfo->ci_flags & (1UL << NILFS_CPINFO_ ##flag)); \
}
+struct nilfs_suinfo {
+ __u64 sui_lastmod;
+ __u32 sui_nblocks;
+ __u32 sui_flags;
+};
+enum {
+ NILFS_SUINFO_ACTIVE,
+ NILFS_SUINFO_DIRTY,
+ NILFS_SUINFO_ERROR,
+};
#define NILFS_SUINFO_FNS(flag,name) static inline int nilfs_suinfo_ ##name(const struct nilfs_suinfo * si) \
{ return si->sui_flags & (1UL << NILFS_SUINFO_ ##flag); \
}
@@ -61,6 +71,15 @@
} static inline int nilfs_suinfo_update_ ##name(const struct nilfs_suinfo_update * sup) \
{ return ! ! (sup->sup_flags & (1UL << NILFS_SUINFO_UPDATE_ ##flag)); \
}
+enum {
+ NILFS_CHECKPOINT,
+ NILFS_SNAPSHOT,
+};
+struct nilfs_cpmode {
+ __u64 cm_cno;
+ __u32 cm_mode;
+ __u32 cm_pad;
+};
struct nilfs_argv {
__u64 v_base;
__u32 v_nmembs;
diff --git a/libc/kernel/uapi/linux/nilfs2_ondisk.h b/libc/kernel/uapi/linux/nilfs2_ondisk.h
index d70b75f..e9995a1 100644
--- a/libc/kernel/uapi/linux/nilfs2_ondisk.h
+++ b/libc/kernel/uapi/linux/nilfs2_ondisk.h
@@ -254,6 +254,11 @@
} static inline int nilfs_checkpoint_ ##name(const struct nilfs_checkpoint * cp) \
{ return ! ! (le32_to_cpu(cp->cp_flags) & (1UL << NILFS_CHECKPOINT_ ##flag)); \
}
+struct nilfs_cpfile_header {
+ __le64 ch_ncheckpoints;
+ __le64 ch_nsnapshots;
+ struct nilfs_snapshot_list ch_snapshot_list;
+};
#define NILFS_CPFILE_FIRST_CHECKPOINT_OFFSET ((sizeof(struct nilfs_cpfile_header) + sizeof(struct nilfs_checkpoint) - 1) / sizeof(struct nilfs_checkpoint))
struct nilfs_segment_usage {
__le64 su_lastmod;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index a6989cf..9dfdbc0 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1446,6 +1446,7 @@
LIBC_Q { # introduced=Q
global:
+ __aeabi_read_tp; # arm
__res_randomid;
android_fdsan_close_with_tag;
android_fdsan_create_owner_tag;
diff --git a/libc/tzcode/bionic.cpp b/libc/tzcode/bionic.cpp
index cb9c359..9051308 100644
--- a/libc/tzcode/bionic.cpp
+++ b/libc/tzcode/bionic.cpp
@@ -228,13 +228,18 @@
if (fd >= 0) return fd;
#else
// On the host, we don't expect those locations to exist, and we're not
- // worried about security so we trust $ANDROID_DATA and $ANDROID_ROOT to
- // point us in the right direction.
+ // worried about security so we trust $ANDROID_DATA, $ANDROID_RUNTIME_ROOT
+ // and $ANDROID_ROOT to point us in the right direction.
char* path = make_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata");
fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
free(path);
if (fd >= 0) return fd;
+ path = make_path("ANDROID_RUNTIME_ROOT", "/etc/tz/tzdata");
+ fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+ free(path);
+ if (fd >= 0) return fd;
+
path = make_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata");
fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
free(path);
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index ac4c1fd..0e75c85 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -241,11 +241,15 @@
if (realpath(value.c_str(), buf)) {
resolved_path = buf;
} else if (errno != ENOENT) {
- DL_WARN("%s:%zd: warning: path \"%s\" couldn't be resolved: %s",
- ld_config_file_path,
- cp.lineno(),
- value.c_str(),
- strerror(errno));
+ // realpath is expected to fail with EPERM in some situations, so log
+ // the failure with INFO rather than DL_WARN. e.g. A binary in
+ // /data/local/tmp may attempt to stat /postinstall. See
+ // http://b/120996057.
+ INFO("%s:%zd: warning: path \"%s\" couldn't be resolved: %s",
+ ld_config_file_path,
+ cp.lineno(),
+ value.c_str(),
+ strerror(errno));
resolved_path = value;
} else {
// ENOENT: no need to test if binary is under the path
diff --git a/tests/Android.bp b/tests/Android.bp
index 90167d1..beed07a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -65,6 +65,7 @@
name: "libBionicStandardTests",
defaults: ["bionic_tests_defaults"],
srcs: [
+ "__aeabi_read_tp_test.cpp",
"alloca_test.cpp",
"android_get_device_api_level.cpp",
"arpa_inet_test.cpp",
diff --git a/tests/__aeabi_read_tp_test.cpp b/tests/__aeabi_read_tp_test.cpp
new file mode 100644
index 0000000..ab96af9
--- /dev/null
+++ b/tests/__aeabi_read_tp_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+
+#include "private/__get_tls.h"
+
+#if defined(__arm__)
+extern "C" void* __aeabi_read_tp();
+TEST(aeabi, read_tp) {
+ ASSERT_EQ(__aeabi_read_tp(), static_cast<void*>(__get_tls()));
+}
+#endif