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