1 // Copyright 2017 Google LLC
2 //
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
15 #include "internal/hwcaps.h"
16
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include "cpu_features_macros.h"
21 #include "internal/filesystem.h"
22 #include "internal/string_view.h"
23
IsSet(const uint32_t mask,const uint32_t value)24 static bool IsSet(const uint32_t mask, const uint32_t value) {
25 if (mask == 0) return false;
26 return (value & mask) == mask;
27 }
28
CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,const HardwareCapabilities hwcaps)29 bool 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.
37 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
38 const char* CpuFeatures_GetPlatformPointer(void);
39 const char* CpuFeatures_GetBasePlatformPointer(void);
40 #else
41
42 // Debug facilities
43 #if defined(NDEBUG)
44 #define D(...)
45 #else
46 #include <stdio.h>
47 #define D(...) \
48 do { \
49 printf(__VA_ARGS__); \
50 fflush(stdout); \
51 } while (0)
52 #endif
53
54 ////////////////////////////////////////////////////////////////////////////////
55 // Implementation of GetElfHwcapFromGetauxval
56 ////////////////////////////////////////////////////////////////////////////////
57
58 #define AT_HWCAP 16
59 #define AT_HWCAP2 26
60 #define AT_PLATFORM 15
61 #define AT_BASE_PLATFORM 24
62
63 #if defined(HAVE_STRONG_GETAUXVAL)
64 #include <sys/auxv.h>
GetElfHwcapFromGetauxval(uint32_t hwcap_type)65 static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
66 return getauxval(hwcap_type);
67 }
68 #elif defined(HAVE_DLFCN_H)
69 // On Android we probe the system's C library for a 'getauxval' function and
70 // call it if it exits, or return 0 for failure. This function is available
71 // since API level 18.
72 //
73 // Note that getauxval() can't really be re-implemented here, because its
74 // implementation does not parse /proc/self/auxv. Instead it depends on values
75 // that are passed by the kernel at process-init time to the C runtime
76 // initialization layer.
77
78 #include <dlfcn.h>
79
80 typedef unsigned long getauxval_func_t(unsigned long);
81
GetElfHwcapFromGetauxval(uint32_t hwcap_type)82 static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
83 uint32_t ret = 0;
84 void *libc_handle = NULL;
85 getauxval_func_t *func = NULL;
86
87 dlerror(); // Cleaning error state before calling dlopen.
88 libc_handle = dlopen("libc.so", RTLD_NOW);
89 if (!libc_handle) {
90 D("Could not dlopen() C library: %s\n", dlerror());
91 return 0;
92 }
93 func = (getauxval_func_t *)dlsym(libc_handle, "getauxval");
94 if (!func) {
95 D("Could not find getauxval() in C library\n");
96 } else {
97 // Note: getauxval() returns 0 on failure. Doesn't touch errno.
98 ret = (uint32_t)(*func)(hwcap_type);
99 }
100 dlclose(libc_handle);
101 return ret;
102 }
103 #else
104 #error "This platform does not provide hardware capabilities."
105 #endif
106
107 // Implementation of GetHardwareCapabilities for OS that provide
108 // GetElfHwcapFromGetauxval().
109
110 // Fallback when getauxval is not available, retrieves hwcaps from
111 // "/proc/self/auxv".
GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type)112 static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
113 struct {
114 uint32_t tag;
115 uint32_t value;
116 } entry;
117 uint32_t result = 0;
118 const char filepath[] = "/proc/self/auxv";
119 const int fd = CpuFeatures_OpenFile(filepath);
120 if (fd < 0) {
121 D("Could not open %s\n", filepath);
122 return 0;
123 }
124 for (;;) {
125 const int ret = CpuFeatures_ReadFile(fd, (char *)&entry, sizeof entry);
126 if (ret < 0) {
127 D("Error while reading %s\n", filepath);
128 break;
129 }
130 // Detect end of list.
131 if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
132 break;
133 }
134 if (entry.tag == hwcap_type) {
135 result = entry.value;
136 break;
137 }
138 }
139 CpuFeatures_CloseFile(fd);
140 return result;
141 }
142
143 // Retrieves hardware capabilities by first trying to call getauxval, if not
144 // available falls back to reading "/proc/self/auxv".
GetHardwareCapabilitiesFor(uint32_t type)145 static unsigned long GetHardwareCapabilitiesFor(uint32_t type) {
146 unsigned long hwcaps = GetElfHwcapFromGetauxval(type);
147 if (!hwcaps) {
148 D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
149 hwcaps = GetElfHwcapFromProcSelfAuxv(type);
150 }
151 return hwcaps;
152 }
153
CpuFeatures_GetHardwareCapabilities(void)154 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
155 HardwareCapabilities capabilities;
156 capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
157 capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
158 return capabilities;
159 }
160
CpuFeatures_GetPlatformPointer(void)161 const char *CpuFeatures_GetPlatformPointer(void) {
162 return (const char *)GetHardwareCapabilitiesFor(AT_PLATFORM);
163 }
164
CpuFeatures_GetBasePlatformPointer(void)165 const char *CpuFeatures_GetBasePlatformPointer(void) {
166 return (const char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM);
167 }
168
169 #endif // CPU_FEATURES_TEST
170