platform_bionic/libc/arch-arm/dynamic_function_dispatch.cpp
Peter Collingbourne 900d07d6a1 Add arm64 string.h function implementations for use with hardware supporting MTE.
As it turns out, our "generic" arm64 implementations of certain string.h
functions are not actually generic, since they will eagerly read memory
possibly outside of the bounds of an MTE granule, which may lead to a segfault
on MTE-enabled hardware. Therefore, move the implementations into a "default"
directory and use ifuncs to select between them and a new set of "mte"
implementations, conditional on whether the hardware and kernel support MTE.

The MTE implementations are currently naive implementations written in C
but will later be replaced with a set of optimized assembly implementations.

Bug: 135772972
Change-Id: Ife37c4e0e6fd60ff20a34594cc09c541af4d1dd7
2019-10-29 16:18:31 -07:00

292 lines
9 KiB
C++

/*
* 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>
#include <private/bionic_ifuncs.h>
extern "C" {
enum CpuVariant {
kUnknown = 0,
kGeneric,
kCortexA7,
kCortexA9,
kCortexA53,
kCortexA55,
kKrait,
kKryo,
};
static constexpr int MAX_CPU_NAME_LEN = 12;
struct CpuVariantNames {
alignas(alignof(int)) char name[MAX_CPU_NAME_LEN];
CpuVariant variant;
};
static constexpr CpuVariantNames cpu_variant_names[] = {
{"cortex-a76", kCortexA55},
{"kryo385", kCortexA55},
{"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;
}
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;
}
static CpuVariant init_cpu_variant() {
int fd = ifunc_open("/dev/cpu_variant:arm");
if (fd < 0) return kGeneric;
alignas(alignof(int)) char name[MAX_CPU_NAME_LEN] = {};
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) {
if (is_same_name(cpu_variant->name, name)) {
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);
DEFINE_IFUNC_FOR(memmove) {
RETURN_FUNC(memmove_func, memmove_a15);
}
typedef void* memcpy_func(void*, const void*, size_t);
DEFINE_IFUNC_FOR(memcpy) {
return memmove_resolver(hwcap);
}
typedef void* __memcpy_func(void*, const void*, size_t);
DEFINE_IFUNC_FOR(__memcpy) {
switch(get_cpu_variant()) {
case kCortexA7:
RETURN_FUNC(__memcpy_func, __memcpy_a7);
case kCortexA9:
RETURN_FUNC(__memcpy_func, __memcpy_a9);
case kKrait:
RETURN_FUNC(__memcpy_func, __memcpy_krait);
case kCortexA53:
RETURN_FUNC(__memcpy_func, __memcpy_a53);
case kCortexA55:
RETURN_FUNC(__memcpy_func, __memcpy_a55);
case kKryo:
RETURN_FUNC(__memcpy_func, __memcpy_kryo);
default:
RETURN_FUNC(__memcpy_func, __memcpy_a15);
}
}
typedef void* __memset_chk_func(void* s, int c, size_t n, size_t n2);
DEFINE_IFUNC_FOR(__memset_chk) {
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);
DEFINE_IFUNC_FOR(memset) {
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);
DEFINE_IFUNC_FOR(strcpy) {
switch(get_cpu_variant()) {
case kCortexA9:
RETURN_FUNC(strcpy_func, strcpy_a9);
default:
RETURN_FUNC(strcpy_func, strcpy_a15);
}
}
typedef char* __strcpy_chk_func(char* dst, const char* src, size_t dst_len);
DEFINE_IFUNC_FOR(__strcpy_chk) {
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:
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a55);
default:
RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a15);
}
}
typedef char* stpcpy_func(char* __dst, const char* __src);
DEFINE_IFUNC_FOR(stpcpy) {
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);
DEFINE_IFUNC_FOR(strcat) {
switch(get_cpu_variant()) {
case kCortexA9:
RETURN_FUNC(strcat_func, strcat_a9);
default:
RETURN_FUNC(strcat_func, strcat_a15);
}
}
typedef char* __strcat_chk_func(char* dst, const char* src, size_t dst_buf_size);
DEFINE_IFUNC_FOR(__strcat_chk) {
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:
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a55);
default:
RETURN_FUNC(__strcat_chk_func, __strcat_chk_a15);
}
}
typedef int strcmp_func(const char* __lhs, const char* __rhs);
DEFINE_IFUNC_FOR(strcmp) {
RETURN_FUNC(strcmp_func, strcmp_a15);
}
typedef size_t strlen_func(const char* __s);
DEFINE_IFUNC_FOR(strlen) {
switch(get_cpu_variant()) {
case kCortexA9:
RETURN_FUNC(strlen_func, strlen_a9);
default:
RETURN_FUNC(strlen_func, strlen_a15);
}
}
} // extern "C"