1 #include <limits.h>
2 #include <string.h>
3
4 #include <dlfcn.h>
5 #include <elf.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #if CPUINFO_MOCK
13 #include <cpuinfo-mock.h>
14 #endif
15 #include <arm/linux/api.h>
16 #include <cpuinfo.h>
17 #include <cpuinfo/log.h>
18
19 #if CPUINFO_ARCH_ARM64 || \
20 CPUINFO_ARCH_ARM && defined(__GLIBC__) && defined(__GLIBC_MINOR__) && \
21 (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)
22 #include <sys/auxv.h>
23 #else
24 #define AT_HWCAP 16
25 #define AT_HWCAP2 26
26 #endif
27
28 #if CPUINFO_MOCK
29 static uint32_t mock_hwcap = 0;
cpuinfo_set_hwcap(uint32_t hwcap)30 void cpuinfo_set_hwcap(uint32_t hwcap) {
31 mock_hwcap = hwcap;
32 }
33
34 static uint64_t mock_hwcap2 = 0;
cpuinfo_set_hwcap2(uint64_t hwcap2)35 void cpuinfo_set_hwcap2(uint64_t hwcap2) {
36 mock_hwcap2 = hwcap2;
37 }
38 #endif
39
40 #if CPUINFO_ARCH_ARM
41 typedef unsigned long (*getauxval_function_t)(unsigned long);
42
cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static1],uint64_t hwcap2[restrict static1])43 bool cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
44 #if CPUINFO_MOCK
45 *hwcap = mock_hwcap;
46 *hwcap2 = mock_hwcap2;
47 return true;
48 #elif defined(__ANDROID__)
49 /* Android: dynamically check if getauxval is supported */
50 void* libc = NULL;
51 getauxval_function_t getauxval = NULL;
52
53 dlerror();
54 libc = dlopen("libc.so", RTLD_LAZY);
55 if (libc == NULL) {
56 cpuinfo_log_warning("failed to load libc.so: %s", dlerror());
57 goto cleanup;
58 }
59
60 getauxval = (getauxval_function_t)dlsym(libc, "getauxval");
61 if (getauxval == NULL) {
62 cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror());
63 goto cleanup;
64 }
65
66 *hwcap = getauxval(AT_HWCAP);
67 *hwcap2 = getauxval(AT_HWCAP2);
68
69 cleanup:
70 if (libc != NULL) {
71 dlclose(libc);
72 libc = NULL;
73 }
74 return getauxval != NULL;
75 #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)
76 /* GNU/Linux: getauxval is supported since glibc-2.16 */
77 *hwcap = getauxval(AT_HWCAP);
78 *hwcap2 = getauxval(AT_HWCAP2);
79 return true;
80 #else
81 return false;
82 #endif
83 }
84
85 #ifdef __ANDROID__
cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static1],uint64_t hwcap2[restrict static1])86 bool cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
87 #if CPUINFO_MOCK
88 *hwcap = mock_hwcap;
89 *hwcap2 = mock_hwcap2;
90 return true;
91 #else
92 uint64_t hwcaps[2] = {0, 0};
93 bool result = false;
94 int file = -1;
95
96 file = open("/proc/self/auxv", O_RDONLY);
97 if (file == -1) {
98 cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno));
99 goto cleanup;
100 }
101
102 ssize_t bytes_read;
103 do {
104 Elf32_auxv_t elf_auxv;
105 bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t));
106 if (bytes_read < 0) {
107 cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno));
108 goto cleanup;
109 } else if (bytes_read > 0) {
110 if (bytes_read == sizeof(elf_auxv)) {
111 switch (elf_auxv.a_type) {
112 case AT_HWCAP:
113 hwcaps[0] = (uint32_t)elf_auxv.a_un.a_val;
114 break;
115 case AT_HWCAP2:
116 hwcaps[1] = (uint64_t)elf_auxv.a_un.a_val;
117 break;
118 }
119 } else {
120 cpuinfo_log_warning(
121 "failed to read %zu bytes from /proc/self/auxv: %zu bytes available",
122 sizeof(elf_auxv),
123 (size_t)bytes_read);
124 goto cleanup;
125 }
126 }
127 } while (bytes_read == sizeof(Elf32_auxv_t));
128
129 /* Success, commit results */
130 *hwcap = hwcaps[0];
131 *hwcap2 = hwcaps[1];
132 result = true;
133
134 cleanup:
135 if (file != -1) {
136 close(file);
137 file = -1;
138 }
139 return result;
140 #endif
141 }
142 #endif /* __ANDROID__ */
143 #elif CPUINFO_ARCH_ARM64
cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static1],uint64_t hwcap2[restrict static1])144 void cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
145 #if CPUINFO_MOCK
146 *hwcap = mock_hwcap;
147 *hwcap2 = mock_hwcap2;
148 #else
149 *hwcap = (uint32_t)getauxval(AT_HWCAP);
150 *hwcap2 = (uint64_t)getauxval(AT_HWCAP2);
151 return;
152 #endif
153 }
154 #endif
155