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 PlatformType CpuFeatures_GetPlatformType(void);
39 #else
40 
41 // Debug facilities
42 #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 
53 ////////////////////////////////////////////////////////////////////////////////
54 // Implementation of GetElfHwcapFromGetauxval
55 ////////////////////////////////////////////////////////////////////////////////
56 
57 #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)
63 #include <sys/auxv.h>
GetElfHwcapFromGetauxval(uint32_t hwcap_type)64 static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
65   return getauxval(hwcap_type);
66 }
67 #elif defined(HAVE_DLFCN_H)
68 // 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.
82 
83 #include <dlfcn.h>
84 
85 typedef unsigned long getauxval_func_t(unsigned long);
86 
GetElfHwcapFromGetauxval(uint32_t hwcap_type)87 static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
88   uint32_t ret = 0;
89   void *libc_handle = NULL;
90   getauxval_func_t *func = NULL;
91 
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   }
98   func = (getauxval_func_t *)dlsym(libc_handle, "getauxval");
99   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 }
108 #else
109 #error "This platform does not provide hardware capabilities."
110 #endif
111 
112 // Implementation of GetHardwareCapabilities for OS that provide
113 // GetElfHwcapFromGetauxval().
114 
115 // Fallback when getauxval is not available, retrieves hwcaps from
116 // "/proc/self/auxv".
GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type)117 static 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";
124   const int fd = CpuFeatures_OpenFile(filepath);
125   if (fd < 0) {
126     D("Could not open %s\n", filepath);
127     return 0;
128   }
129   for (;;) {
130     const int ret = CpuFeatures_ReadFile(fd, (char *)&entry, sizeof entry);
131     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   }
144   CpuFeatures_CloseFile(fd);
145   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".
GetHardwareCapabilitiesFor(uint32_t type)150 static unsigned long GetHardwareCapabilitiesFor(uint32_t type) {
151   unsigned long hwcaps = GetElfHwcapFromGetauxval(type);
152   if (!hwcaps) {
153     D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
154     hwcaps = GetElfHwcapFromProcSelfAuxv(type);
155   }
156   return hwcaps;
157 }
158 
CpuFeatures_GetHardwareCapabilities(void)159 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
160   HardwareCapabilities capabilities;
161   capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
162   capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
163   return capabilities;
164 }
165 
166 PlatformType kEmptyPlatformType;
167 
CpuFeatures_GetPlatformType(void)168 PlatformType 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 }
181 
182 #endif  // CPU_FEATURES_TEST
183