1 | //===- bolt/runtime/common.h ------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #if defined(__linux__) |
10 | |
11 | #include <cstddef> |
12 | #include <cstdint> |
13 | |
14 | #include "config.h" |
15 | |
16 | #ifdef HAVE_ELF_H |
17 | #include <elf.h> |
18 | #endif |
19 | |
20 | #elif defined(__APPLE__) |
21 | |
22 | typedef __SIZE_TYPE__ size_t; |
23 | #define __SSIZE_TYPE__ \ |
24 | __typeof__(_Generic((__SIZE_TYPE__)0, unsigned long long int \ |
25 | : (long long int)0, unsigned long int \ |
26 | : (long int)0, unsigned int \ |
27 | : (int)0, unsigned short \ |
28 | : (short)0, unsigned char \ |
29 | : (signed char)0)) |
30 | typedef __SSIZE_TYPE__ ssize_t; |
31 | |
32 | typedef unsigned long long uint64_t; |
33 | typedef unsigned uint32_t; |
34 | typedef unsigned char uint8_t; |
35 | |
36 | typedef long long int64_t; |
37 | typedef int int32_t; |
38 | |
39 | #else |
40 | #error "For Linux or MacOS only" |
41 | #endif |
42 | |
43 | #define PROT_READ 0x1 /* Page can be read. */ |
44 | #define PROT_WRITE 0x2 /* Page can be written. */ |
45 | #define PROT_EXEC 0x4 /* Page can be executed. */ |
46 | #define PROT_NONE 0x0 /* Page can not be accessed. */ |
47 | #define PROT_GROWSDOWN \ |
48 | 0x01000000 /* Extend change to start of \ |
49 | growsdown vma (mprotect only). */ |
50 | #define PROT_GROWSUP \ |
51 | 0x02000000 /* Extend change to start of \ |
52 | growsup vma (mprotect only). */ |
53 | |
54 | /* Sharing types (must choose one and only one of these). */ |
55 | #define MAP_SHARED 0x01 /* Share changes. */ |
56 | #define MAP_PRIVATE 0x02 /* Changes are private. */ |
57 | #define MAP_FIXED 0x10 /* Interpret addr exactly. */ |
58 | |
59 | #if defined(__APPLE__) |
60 | #define MAP_ANONYMOUS 0x1000 |
61 | #else |
62 | #define MAP_ANONYMOUS 0x20 |
63 | #endif |
64 | |
65 | #define MAP_FAILED ((void *)-1) |
66 | |
67 | #define SEEK_SET 0 /* Seek from beginning of file. */ |
68 | #define SEEK_CUR 1 /* Seek from current position. */ |
69 | #define SEEK_END 2 /* Seek from end of file. */ |
70 | |
71 | #define O_RDONLY 0 |
72 | #define O_WRONLY 1 |
73 | #define O_RDWR 2 |
74 | #define O_CREAT 64 |
75 | #define O_TRUNC 512 |
76 | #define O_APPEND 1024 |
77 | |
78 | // Functions that are required by freestanding environment. Compiler may |
79 | // generate calls to these implicitly. |
80 | extern "C" { |
81 | void *memcpy(void *Dest, const void *Src, size_t Len) { |
82 | uint8_t *d = static_cast<uint8_t *>(Dest); |
83 | const uint8_t *s = static_cast<const uint8_t *>(Src); |
84 | while (Len--) |
85 | *d++ = *s++; |
86 | return Dest; |
87 | } |
88 | |
89 | void *memmove(void *Dest, const void *Src, size_t Len) { |
90 | uint8_t *d = static_cast<uint8_t *>(Dest); |
91 | const uint8_t *s = static_cast<const uint8_t *>(Src); |
92 | if (d < s) { |
93 | while (Len--) |
94 | *d++ = *s++; |
95 | } else { |
96 | s += Len - 1; |
97 | d += Len - 1; |
98 | while (Len--) |
99 | *d-- = *s--; |
100 | } |
101 | |
102 | return Dest; |
103 | } |
104 | |
105 | void *memset(void *Buf, int C, size_t Size) { |
106 | char *S = (char *)Buf; |
107 | for (size_t I = 0; I < Size; ++I) |
108 | *S++ = C; |
109 | return Buf; |
110 | } |
111 | |
112 | int memcmp(const void *s1, const void *s2, size_t n) { |
113 | const uint8_t *c1 = static_cast<const uint8_t *>(s1); |
114 | const uint8_t *c2 = static_cast<const uint8_t *>(s2); |
115 | for (; n--; c1++, c2++) { |
116 | if (*c1 != *c2) |
117 | return *c1 < *c2 ? -1 : 1; |
118 | } |
119 | return 0; |
120 | } |
121 | } // extern "C" |
122 | |
123 | // Anonymous namespace covering everything but our library entry point |
124 | namespace { |
125 | |
126 | struct dirent64 { |
127 | uint64_t d_ino; /* Inode number */ |
128 | int64_t d_off; /* Offset to next linux_dirent */ |
129 | unsigned short d_reclen; /* Length of this linux_dirent */ |
130 | unsigned char d_type; |
131 | char d_name[]; /* Filename (null-terminated) */ |
132 | /* length is actually (d_reclen - 2 - |
133 | offsetof(struct linux_dirent, d_name)) */ |
134 | }; |
135 | |
136 | /* Length of the entries in `struct utsname' is 65. */ |
137 | #define _UTSNAME_LENGTH 65 |
138 | |
139 | struct UtsNameTy { |
140 | char sysname[_UTSNAME_LENGTH]; /* Operating system name (e.g., "Linux") */ |
141 | char nodename[_UTSNAME_LENGTH]; /* Name within "some implementation-defined |
142 | network" */ |
143 | char release[_UTSNAME_LENGTH]; /* Operating system release (e.g., "2.6.28") */ |
144 | char version[_UTSNAME_LENGTH]; /* Operating system version */ |
145 | char machine[_UTSNAME_LENGTH]; /* Hardware identifier */ |
146 | char domainname[_UTSNAME_LENGTH]; /* NIS or YP domain name */ |
147 | }; |
148 | |
149 | struct timespec { |
150 | uint64_t tv_sec; /* seconds */ |
151 | uint64_t tv_nsec; /* nanoseconds */ |
152 | }; |
153 | |
154 | #if defined(__aarch64__) |
155 | #include "sys_aarch64.h" |
156 | #else |
157 | #include "sys_x86_64.h" |
158 | #endif |
159 | |
160 | constexpr uint32_t BufSize = 10240; |
161 | |
162 | // Helper functions for writing strings to the .fdata file. We intentionally |
163 | // avoid using libc names to make it clear it is our impl. |
164 | |
165 | /// Write number Num using Base to the buffer in OutBuf, returns a pointer to |
166 | /// the end of the string. |
167 | char *intToStr(char *OutBuf, uint64_t Num, uint32_t Base) { |
168 | const char *Chars = "0123456789abcdef" ; |
169 | char Buf[21]; |
170 | char *Ptr = Buf; |
171 | while (Num) { |
172 | *Ptr++ = *(Chars + (Num % Base)); |
173 | Num /= Base; |
174 | } |
175 | if (Ptr == Buf) { |
176 | *OutBuf++ = '0'; |
177 | return OutBuf; |
178 | } |
179 | while (Ptr != Buf) |
180 | *OutBuf++ = *--Ptr; |
181 | |
182 | return OutBuf; |
183 | } |
184 | |
185 | /// Copy Str to OutBuf, returns a pointer to the end of the copied string |
186 | char *strCopy(char *OutBuf, const char *Str, int32_t Size = BufSize) { |
187 | while (*Str) { |
188 | *OutBuf++ = *Str++; |
189 | if (--Size <= 0) |
190 | return OutBuf; |
191 | } |
192 | return OutBuf; |
193 | } |
194 | |
195 | /// Compare two strings, at most Num bytes. |
196 | int strnCmp(const char *Str1, const char *Str2, size_t Num) { |
197 | while (Num && *Str1 && (*Str1 == *Str2)) { |
198 | Num--; |
199 | Str1++; |
200 | Str2++; |
201 | } |
202 | if (Num == 0) |
203 | return 0; |
204 | return *(unsigned char *)Str1 - *(unsigned char *)Str2; |
205 | } |
206 | |
207 | uint32_t strLen(const char *Str) { |
208 | uint32_t Size = 0; |
209 | while (*Str++) |
210 | ++Size; |
211 | return Size; |
212 | } |
213 | |
214 | void *strStr(const char *const Haystack, const char *const Needle) { |
215 | int j = 0; |
216 | |
217 | for (int i = 0; i < strLen(Str: Haystack); i++) { |
218 | if (Haystack[i] == Needle[0]) { |
219 | for (j = 1; j < strLen(Str: Needle); j++) { |
220 | if (Haystack[i + j] != Needle[j]) |
221 | break; |
222 | } |
223 | if (j == strLen(Str: Needle)) |
224 | return (void *)&Haystack[i]; |
225 | } |
226 | } |
227 | return nullptr; |
228 | } |
229 | |
230 | void reportNumber(const char *Msg, uint64_t Num, uint32_t Base) { |
231 | char Buf[BufSize]; |
232 | char *Ptr = Buf; |
233 | Ptr = strCopy(OutBuf: Ptr, Str: Msg, Size: BufSize - 23); |
234 | Ptr = intToStr(OutBuf: Ptr, Num, Base); |
235 | Ptr = strCopy(OutBuf: Ptr, Str: "\n" ); |
236 | __write(fd: 2, buf: Buf, count: Ptr - Buf); |
237 | } |
238 | |
239 | void report(const char *Msg) { __write(fd: 2, buf: Msg, count: strLen(Str: Msg)); } |
240 | |
241 | unsigned long hexToLong(const char *Str, char Terminator = '\0') { |
242 | unsigned long Res = 0; |
243 | while (*Str != Terminator) { |
244 | Res <<= 4; |
245 | if ('0' <= *Str && *Str <= '9') |
246 | Res += *Str++ - '0'; |
247 | else if ('a' <= *Str && *Str <= 'f') |
248 | Res += *Str++ - 'a' + 10; |
249 | else if ('A' <= *Str && *Str <= 'F') |
250 | Res += *Str++ - 'A' + 10; |
251 | else |
252 | return 0; |
253 | } |
254 | return Res; |
255 | } |
256 | |
257 | /// Starting from character at \p buf, find the longest consecutive sequence |
258 | /// of digits (0-9) and convert it to uint32_t. The converted value |
259 | /// is put into \p ret. \p end marks the end of the buffer to avoid buffer |
260 | /// overflow. The function \returns whether a valid uint32_t value is found. |
261 | /// \p buf will be updated to the next character right after the digits. |
262 | static bool scanUInt32(const char *&Buf, const char *End, uint32_t &Ret) { |
263 | uint64_t Result = 0; |
264 | const char *OldBuf = Buf; |
265 | while (Buf < End && ((*Buf) >= '0' && (*Buf) <= '9')) { |
266 | Result = Result * 10 + (*Buf) - '0'; |
267 | ++Buf; |
268 | } |
269 | if (OldBuf != Buf && Result <= 0xFFFFFFFFu) { |
270 | Ret = static_cast<uint32_t>(Result); |
271 | return true; |
272 | } |
273 | return false; |
274 | } |
275 | |
276 | void reportError(const char *Msg, uint64_t Size) { |
277 | __write(fd: 2, buf: Msg, count: Size); |
278 | __exit(code: 1); |
279 | } |
280 | |
281 | void assert(bool Assertion, const char *Msg) { |
282 | if (Assertion) |
283 | return; |
284 | char Buf[BufSize]; |
285 | char *Ptr = Buf; |
286 | Ptr = strCopy(OutBuf: Ptr, Str: "Assertion failed: " ); |
287 | Ptr = strCopy(OutBuf: Ptr, Str: Msg, Size: BufSize - 40); |
288 | Ptr = strCopy(OutBuf: Ptr, Str: "\n" ); |
289 | reportError(Msg: Buf, Size: Ptr - Buf); |
290 | } |
291 | |
292 | #define SIG_BLOCK 0 |
293 | #define SIG_UNBLOCK 1 |
294 | #define SIG_SETMASK 2 |
295 | |
296 | static const uint64_t MaskAllSignals[] = {-1ULL}; |
297 | |
298 | class Mutex { |
299 | volatile bool InUse{false}; |
300 | |
301 | public: |
302 | bool acquire() { return !__atomic_test_and_set(&InUse, __ATOMIC_ACQUIRE); } |
303 | void release() { __atomic_clear(&InUse, __ATOMIC_RELEASE); } |
304 | }; |
305 | |
306 | /// RAII wrapper for Mutex |
307 | class Lock { |
308 | Mutex &M; |
309 | uint64_t SignalMask[1] = {}; |
310 | |
311 | public: |
312 | Lock(Mutex &M) : M(M) { |
313 | __sigprocmask(SIG_BLOCK, set: MaskAllSignals, oldset: SignalMask); |
314 | while (!M.acquire()) { |
315 | } |
316 | } |
317 | |
318 | ~Lock() { |
319 | M.release(); |
320 | __sigprocmask(SIG_SETMASK, set: SignalMask, oldset: nullptr); |
321 | } |
322 | }; |
323 | |
324 | /// RAII wrapper for Mutex |
325 | class TryLock { |
326 | Mutex &M; |
327 | bool Locked = false; |
328 | |
329 | public: |
330 | TryLock(Mutex &M) : M(M) { |
331 | int Retry = 100; |
332 | while (--Retry && !M.acquire()) |
333 | ; |
334 | if (Retry) |
335 | Locked = true; |
336 | } |
337 | bool isLocked() { return Locked; } |
338 | |
339 | ~TryLock() { |
340 | if (isLocked()) |
341 | M.release(); |
342 | } |
343 | }; |
344 | |
345 | inline uint64_t alignTo(uint64_t Value, uint64_t Align) { |
346 | return (Value + Align - 1) / Align * Align; |
347 | } |
348 | |
349 | } // anonymous namespace |
350 | |