2018-11-19 20:00:32 +01:00
|
|
|
/*
|
|
|
|
* 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 <fcntl.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
|
2019-09-17 18:53:14 +02:00
|
|
|
#include <private/bionic_ifuncs.h>
|
|
|
|
|
2018-11-19 20:00:32 +01:00
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
enum CpuVariant {
|
|
|
|
kUnknown = 0,
|
|
|
|
kGeneric,
|
|
|
|
kCortexA7,
|
|
|
|
kCortexA9,
|
|
|
|
kCortexA53,
|
|
|
|
kCortexA55,
|
|
|
|
kKrait,
|
|
|
|
kKryo,
|
|
|
|
};
|
|
|
|
|
|
|
|
static constexpr int MAX_CPU_NAME_LEN = 12;
|
|
|
|
struct CpuVariantNames {
|
2018-11-29 07:56:58 +01:00
|
|
|
alignas(alignof(int)) char name[MAX_CPU_NAME_LEN];
|
2018-11-19 20:00:32 +01:00
|
|
|
CpuVariant variant;
|
|
|
|
};
|
|
|
|
|
|
|
|
static constexpr CpuVariantNames cpu_variant_names[] = {
|
|
|
|
{"cortex-a76", kCortexA55},
|
2018-12-04 21:54:43 +01:00
|
|
|
{"kryo385", kCortexA55},
|
2018-11-19 20:00:32 +01:00
|
|
|
{"cortex-a75", kCortexA55},
|
|
|
|
{"kryo", kKryo},
|
|
|
|
{"cortex-a73", kCortexA55},
|
|
|
|
{"cortex-a55", kCortexA55},
|
|
|
|
{"cortex-a53", kCortexA53},
|
|
|
|
{"krait", kKrait},
|
|
|
|
{"cortex-a9", kCortexA9},
|
|
|
|
{"cortex-a7", kCortexA7},
|
|
|
|
// kUnknown indicates the end of this array.
|
|
|
|
{"", kUnknown},
|
|
|
|
};
|
|
|
|
|
|
|
|
static long ifunc_open(const char* pathname) {
|
|
|
|
register long r0 __asm__("r0") = AT_FDCWD;
|
|
|
|
register long r1 __asm__("r1") = reinterpret_cast<long>(pathname);
|
|
|
|
register long r2 __asm__("r2") = O_RDONLY;
|
|
|
|
register long r3 __asm__("r3") = 0;
|
|
|
|
register long r7 __asm__("r7") = __NR_openat;
|
|
|
|
__asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r7));
|
|
|
|
return r0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t ifunc_read(int fd, void* buf, size_t count) {
|
|
|
|
register long r0 __asm__("r0") = fd;
|
|
|
|
register long r1 __asm__("r1") = reinterpret_cast<long>(buf);
|
|
|
|
register long r2 __asm__("r2") = count;
|
|
|
|
register long r7 __asm__("r7") = __NR_read;
|
|
|
|
__asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2), "r"(r7) : "memory");
|
|
|
|
return r0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ifunc_close(int fd) {
|
|
|
|
register long r0 __asm__("r0") = fd;
|
|
|
|
register long r7 __asm__("r7") = __NR_close;
|
|
|
|
__asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r7));
|
|
|
|
return r0;
|
|
|
|
}
|
|
|
|
|
2018-11-29 07:56:58 +01:00
|
|
|
static bool is_same_name(const char* a, const char* b) {
|
|
|
|
static_assert(MAX_CPU_NAME_LEN % sizeof(int) == 0, "");
|
|
|
|
const int* ia = reinterpret_cast<const int*>(a);
|
|
|
|
const int* ib = reinterpret_cast<const int*>(b);
|
|
|
|
for (size_t i = 0; i < MAX_CPU_NAME_LEN / sizeof(int); ++i) {
|
|
|
|
if (ia[i] != ib[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-19 20:00:32 +01:00
|
|
|
static CpuVariant init_cpu_variant() {
|
|
|
|
int fd = ifunc_open("/dev/cpu_variant:arm");
|
|
|
|
if (fd < 0) return kGeneric;
|
|
|
|
|
2018-11-29 07:56:58 +01:00
|
|
|
alignas(alignof(int)) char name[MAX_CPU_NAME_LEN] = {};
|
2018-11-19 20:00:32 +01:00
|
|
|
|
|
|
|
int bytes_read, total_read = 0;
|
|
|
|
while (total_read < MAX_CPU_NAME_LEN - 1 &&
|
|
|
|
(bytes_read = ifunc_read(fd, name + total_read,
|
|
|
|
MAX_CPU_NAME_LEN - 1 - total_read)) > 0) {
|
|
|
|
total_read += bytes_read;
|
|
|
|
}
|
|
|
|
ifunc_close(fd);
|
|
|
|
|
|
|
|
if (bytes_read != 0) {
|
|
|
|
// The file is too big. We haven't reach the end. Or maybe there is an
|
|
|
|
// error when reading.
|
|
|
|
return kGeneric;
|
|
|
|
}
|
|
|
|
name[total_read] = 0;
|
|
|
|
|
|
|
|
const CpuVariantNames* cpu_variant = cpu_variant_names;
|
|
|
|
while (cpu_variant->variant != kUnknown) {
|
2018-11-29 07:56:58 +01:00
|
|
|
if (is_same_name(cpu_variant->name, name)) {
|
2018-11-19 20:00:32 +01:00
|
|
|
return cpu_variant->variant;
|
|
|
|
}
|
|
|
|
cpu_variant++;
|
|
|
|
}
|
|
|
|
return kGeneric;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CpuVariant get_cpu_variant() {
|
|
|
|
static CpuVariant cpu_variant = kUnknown;
|
|
|
|
if (cpu_variant == kUnknown) {
|
|
|
|
cpu_variant = init_cpu_variant();
|
|
|
|
}
|
|
|
|
return cpu_variant;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void* memmove_func(void* __dst, const void* __src, size_t __n);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(memmove) {
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(memmove_func, memmove_a15);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void* memcpy_func(void*, const void*, size_t);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(memcpy) {
|
2019-10-28 21:11:00 +01:00
|
|
|
return memmove_resolver(hwcap);
|
2018-12-04 00:05:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef void* __memcpy_func(void*, const void*, size_t);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(__memcpy) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA7:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_a7);
|
2018-11-19 20:00:32 +01:00
|
|
|
case kCortexA9:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_a9);
|
2018-11-19 20:00:32 +01:00
|
|
|
case kKrait:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_krait);
|
2018-11-19 20:00:32 +01:00
|
|
|
case kCortexA53:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_a53);
|
2018-11-19 20:00:32 +01:00
|
|
|
case kCortexA55:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_a55);
|
2018-11-19 20:00:32 +01:00
|
|
|
case kKryo:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_kryo);
|
2018-11-19 20:00:32 +01:00
|
|
|
default:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__memcpy_func, __memcpy_a15);
|
2018-11-19 20:00:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void* __memset_chk_func(void* s, int c, size_t n, size_t n2);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(__memset_chk) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA7:
|
|
|
|
case kCortexA53:
|
|
|
|
case kCortexA55:
|
|
|
|
case kKryo:
|
|
|
|
RETURN_FUNC(__memset_chk_func, __memset_chk_a7);
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(__memset_chk_func, __memset_chk_a9);
|
|
|
|
case kKrait:
|
|
|
|
RETURN_FUNC(__memset_chk_func, __memset_chk_krait);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(__memset_chk_func, __memset_chk_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void* memset_func(void* __dst, int __ch, size_t __n);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(memset) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA7:
|
|
|
|
case kCortexA53:
|
|
|
|
case kCortexA55:
|
|
|
|
case kKryo:
|
|
|
|
RETURN_FUNC(memset_func, memset_a7);
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(memset_func, memset_a9);
|
|
|
|
case kKrait:
|
|
|
|
RETURN_FUNC(memset_func, memset_krait);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(memset_func, memset_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef char* strcpy_func(char* __dst, const char* __src);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(strcpy) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(strcpy_func, strcpy_a9);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(strcpy_func, strcpy_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-28 00:38:47 +01:00
|
|
|
typedef char* __strcpy_chk_func(char* dst, const char* src, size_t dst_len);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(__strcpy_chk) {
|
2018-11-28 00:38:47 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA7:
|
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a7);
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a9);
|
|
|
|
case kKrait:
|
|
|
|
case kKryo:
|
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_krait);
|
|
|
|
case kCortexA53:
|
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a53);
|
|
|
|
case kCortexA55:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a55);
|
2018-11-28 00:38:47 +01:00
|
|
|
default:
|
|
|
|
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-19 20:00:32 +01:00
|
|
|
typedef char* stpcpy_func(char* __dst, const char* __src);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(stpcpy) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(stpcpy_func, stpcpy_a9);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(stpcpy_func, stpcpy_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef char* strcat_func(char* __dst, const char* __src);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(strcat) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(strcat_func, strcat_a9);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(strcat_func, strcat_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-28 00:38:47 +01:00
|
|
|
typedef char* __strcat_chk_func(char* dst, const char* src, size_t dst_buf_size);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(__strcat_chk) {
|
2018-11-28 00:38:47 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA7:
|
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a7);
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a9);
|
|
|
|
case kKrait:
|
|
|
|
case kKryo:
|
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_krait);
|
|
|
|
case kCortexA53:
|
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a53);
|
|
|
|
case kCortexA55:
|
2018-12-04 00:05:16 +01:00
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a55);
|
2018-11-28 00:38:47 +01:00
|
|
|
default:
|
|
|
|
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-19 20:00:32 +01:00
|
|
|
typedef int strcmp_func(const char* __lhs, const char* __rhs);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(strcmp) {
|
2017-05-07 05:22:58 +02:00
|
|
|
RETURN_FUNC(strcmp_func, strcmp_a15);
|
2018-11-19 20:00:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef size_t strlen_func(const char* __s);
|
2019-09-17 18:53:14 +02:00
|
|
|
DEFINE_IFUNC_FOR(strlen) {
|
2018-11-19 20:00:32 +01:00
|
|
|
switch(get_cpu_variant()) {
|
|
|
|
case kCortexA9:
|
|
|
|
RETURN_FUNC(strlen_func, strlen_a9);
|
|
|
|
default:
|
|
|
|
RETURN_FUNC(strlen_func, strlen_a15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // extern "C"
|