blob: dd17e3bc6c92b6b3b94f572d16505a8ed4e67eca [file] [log] [blame]
Guillaume Chatelet3cc8f312020-10-12 08:55:20 +00001// Copyright 2017 Google LLC
Guillaume Chatelet439d3712018-02-01 10:03:09 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Guillaume Chatelet22a53622020-09-23 11:52:20 +020015#include "internal/hwcaps.h"
16
Rashmica Guptac45e32f2018-05-02 14:30:25 +100017#include <stdlib.h>
18#include <string.h>
19
Guillaume Chatelet439d3712018-02-01 10:03:09 +010020#include "cpu_features_macros.h"
21#include "internal/filesystem.h"
Rashmica Guptac45e32f2018-05-02 14:30:25 +100022#include "internal/string_view.h"
Guillaume Chatelet439d3712018-02-01 10:03:09 +010023
Guillaume Chatelet9a8f04b2020-10-12 11:50:35 +020024static bool IsSet(const uint32_t mask, const uint32_t value) {
25 if (mask == 0) return false;
26 return (value & mask) == mask;
27}
28
29bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
30 const HardwareCapabilities hwcaps) {
31 return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) ||
32 IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
33}
34
35#ifdef CPU_FEATURES_TEST
36// In test mode, hwcaps_for_testing will define the following functions.
37HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
38PlatformType CpuFeatures_GetPlatformType(void);
39#else
40
41// Debug facilities
Guillaume Chatelet439d3712018-02-01 10:03:09 +010042#if defined(NDEBUG)
43#define D(...)
44#else
45#include <stdio.h>
46#define D(...) \
47 do { \
48 printf(__VA_ARGS__); \
49 fflush(stdout); \
50 } while (0)
51#endif
52
Guillaume Chatelet439d3712018-02-01 10:03:09 +010053////////////////////////////////////////////////////////////////////////////////
54// Implementation of GetElfHwcapFromGetauxval
55////////////////////////////////////////////////////////////////////////////////
56
Guillaume Chatelet9a8f04b2020-10-12 11:50:35 +020057#define AT_HWCAP 16
58#define AT_HWCAP2 26
59#define AT_PLATFORM 15
60#define AT_BASE_PLATFORM 24
61
62#if defined(HAVE_STRONG_GETAUXVAL)
Guillaume Chatelet122b0672019-01-15 15:18:08 +010063#include <sys/auxv.h>
64static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
65 return getauxval(hwcap_type);
66}
67#elif defined(HAVE_DLFCN_H)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010068// On Android we probe the system's C library for a 'getauxval' function and
69// call it if it exits, or return 0 for failure. This function is available
70// since API level 20.
71//
72// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the edge
73// case where some NDK developers use headers for a platform that is newer than
74// the one really targetted by their application. This is typically done to use
75// newer native APIs only when running on more recent Android versions, and
76// requires careful symbol management.
77//
78// Note that getauxval() can't really be re-implemented here, because its
79// implementation does not parse /proc/self/auxv. Instead it depends on values
80// that are passed by the kernel at process-init time to the C runtime
81// initialization layer.
Guillaume Chatelete8e56102019-01-15 10:52:56 +010082
Guillaume Chatelet439d3712018-02-01 10:03:09 +010083#include <dlfcn.h>
Rashmica Guptac45e32f2018-05-02 14:30:25 +100084
Guillaume Chatelet439d3712018-02-01 10:03:09 +010085typedef unsigned long getauxval_func_t(unsigned long);
86
87static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
88 uint32_t ret = 0;
Guillaume Chatelet22a53622020-09-23 11:52:20 +020089 void *libc_handle = NULL;
90 getauxval_func_t *func = NULL;
Guillaume Chatelet439d3712018-02-01 10:03:09 +010091
92 dlerror(); // Cleaning error state before calling dlopen.
93 libc_handle = dlopen("libc.so", RTLD_NOW);
94 if (!libc_handle) {
95 D("Could not dlopen() C library: %s\n", dlerror());
96 return 0;
97 }
Guillaume Chatelet22a53622020-09-23 11:52:20 +020098 func = (getauxval_func_t *)dlsym(libc_handle, "getauxval");
Guillaume Chatelet439d3712018-02-01 10:03:09 +010099 if (!func) {
100 D("Could not find getauxval() in C library\n");
101 } else {
102 // Note: getauxval() returns 0 on failure. Doesn't touch errno.
103 ret = (uint32_t)(*func)(hwcap_type);
104 }
105 dlclose(libc_handle);
106 return ret;
107}
Guillaume Chatelete8e56102019-01-15 10:52:56 +0100108#else
109#error "This platform does not provide hardware capabilities."
110#endif
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100111
Guillaume Chatelete8e56102019-01-15 10:52:56 +0100112// Implementation of GetHardwareCapabilities for OS that provide
113// GetElfHwcapFromGetauxval().
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100114
115// Fallback when getauxval is not available, retrieves hwcaps from
116// "/proc/self/auxv".
117static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
118 struct {
119 uint32_t tag;
120 uint32_t value;
121 } entry;
122 uint32_t result = 0;
123 const char filepath[] = "/proc/self/auxv";
Arvid Gerstmann235d57c2018-05-04 09:30:32 +0200124 const int fd = CpuFeatures_OpenFile(filepath);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100125 if (fd < 0) {
126 D("Could not open %s\n", filepath);
127 return 0;
128 }
129 for (;;) {
Guillaume Chatelet22a53622020-09-23 11:52:20 +0200130 const int ret = CpuFeatures_ReadFile(fd, (char *)&entry, sizeof entry);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100131 if (ret < 0) {
132 D("Error while reading %s\n", filepath);
133 break;
134 }
135 // Detect end of list.
136 if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
137 break;
138 }
139 if (entry.tag == hwcap_type) {
140 result = entry.value;
141 break;
142 }
143 }
Arvid Gerstmann235d57c2018-05-04 09:30:32 +0200144 CpuFeatures_CloseFile(fd);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100145 return result;
146}
147
148// Retrieves hardware capabilities by first trying to call getauxval, if not
149// available falls back to reading "/proc/self/auxv".
Rashmica Gupta1c8bf0e2018-04-27 11:53:51 +1000150static unsigned long GetHardwareCapabilitiesFor(uint32_t type) {
151 unsigned long hwcaps = GetElfHwcapFromGetauxval(type);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100152 if (!hwcaps) {
153 D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
154 hwcaps = GetElfHwcapFromProcSelfAuxv(type);
155 }
156 return hwcaps;
157}
158
Arvid Gerstmannfd483902018-05-03 17:07:07 +0200159HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100160 HardwareCapabilities capabilities;
161 capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
162 capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
163 return capabilities;
164}
165
Rashmica Guptac45e32f2018-05-02 14:30:25 +1000166PlatformType kEmptyPlatformType;
167
168PlatformType CpuFeatures_GetPlatformType(void) {
169 PlatformType type = kEmptyPlatformType;
170 char *platform = (char *)GetHardwareCapabilitiesFor(AT_PLATFORM);
171 char *base_platform = (char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM);
172
173 if (platform != NULL)
174 CpuFeatures_StringView_CopyString(str(platform), type.platform,
175 sizeof(type.platform));
176 if (base_platform != NULL)
177 CpuFeatures_StringView_CopyString(str(base_platform), type.base_platform,
178 sizeof(type.base_platform));
179 return type;
180}
Guillaume Chatelet9a8f04b2020-10-12 11:50:35 +0200181
182#endif // CPU_FEATURES_TEST