xref: /llvm-project/bolt/runtime/common.h (revision c6799a689df719ca7ceee53774691bf6ac839157)
1 #include <cstddef>
2 #include <cstdint>
3 
4 #include "config.h"
5 #ifdef HAVE_ELF_H
6 #include <elf.h>
7 #endif
8 
9 // Save all registers while keeping 16B stack alignment
10 #define SAVE_ALL                                                               \
11   "push %%rax\n"                                                               \
12   "push %%rbx\n"                                                               \
13   "push %%rcx\n"                                                               \
14   "push %%rdx\n"                                                               \
15   "push %%rdi\n"                                                               \
16   "push %%rsi\n"                                                               \
17   "push %%rbp\n"                                                               \
18   "push %%r8\n"                                                                \
19   "push %%r9\n"                                                                \
20   "push %%r10\n"                                                               \
21   "push %%r11\n"                                                               \
22   "push %%r12\n"                                                               \
23   "push %%r13\n"                                                               \
24   "push %%r14\n"                                                               \
25   "push %%r15\n"                                                               \
26   "sub $8, %%rsp\n"
27 
28 // Mirrors SAVE_ALL
29 #define RESTORE_ALL                                                            \
30   "add $8, %%rsp\n"                                                            \
31   "pop %%r15\n"                                                                \
32   "pop %%r14\n"                                                                \
33   "pop %%r13\n"                                                                \
34   "pop %%r12\n"                                                                \
35   "pop %%r11\n"                                                                \
36   "pop %%r10\n"                                                                \
37   "pop %%r9\n"                                                                 \
38   "pop %%r8\n"                                                                 \
39   "pop %%rbp\n"                                                                \
40   "pop %%rsi\n"                                                                \
41   "pop %%rdi\n"                                                                \
42   "pop %%rdx\n"                                                                \
43   "pop %%rcx\n"                                                                \
44   "pop %%rbx\n"                                                                \
45   "pop %%rax\n"
46 
47 // Anonymous namespace covering everything but our library entry point
48 namespace {
49 
50 // We use a stack-allocated buffer for string manipulation in many pieces of
51 // this code, including the code that prints each line of the fdata file. This
52 // buffer needs to accomodate large function names, but shouldn't be arbitrarily
53 // large (dynamically allocated) for simplicity of our memory space usage.
54 constexpr uint32_t BufSize = 10240;
55 
56 // Declare some syscall wrappers we use throughout this code to avoid linking
57 // against system libc.
58 uint64_t __open(const char *pathname, uint64_t flags, uint64_t mode) {
59   uint64_t ret;
60   __asm__ __volatile__("movq $2, %%rax\n"
61                        "syscall"
62                        : "=a"(ret)
63                        : "D"(pathname), "S"(flags), "d"(mode)
64                        : "cc", "rcx", "r11", "memory");
65   return ret;
66 }
67 
68 uint64_t __write(uint64_t fd, const void *buf, uint64_t count) {
69   uint64_t ret;
70   __asm__ __volatile__("movq $1, %%rax\n"
71                        "syscall\n"
72                        : "=a"(ret)
73                        : "D"(fd), "S"(buf), "d"(count)
74                        : "cc", "rcx", "r11", "memory");
75   return ret;
76 }
77 
78 uint64_t __lseek(uint64_t fd, uint64_t pos, uint64_t whence) {
79   uint64_t ret;
80   __asm__ __volatile__("movq $8, %%rax\n"
81                        "syscall\n"
82                        : "=a"(ret)
83                        : "D"(fd), "S"(pos), "d"(whence)
84                        : "cc", "rcx", "r11", "memory");
85   return ret;
86 }
87 
88 int __close(uint64_t fd) {
89   uint64_t ret;
90   __asm__ __volatile__("movq $3, %%rax\n"
91                        "syscall\n"
92                        : "=a"(ret)
93                        : "D"(fd)
94                        : "cc", "rcx", "r11", "memory");
95   return ret;
96 }
97 
98 int __madvise(void *addr, size_t length, int advice) {
99   int ret;
100   __asm__ __volatile__("movq $28, %%rax\n"
101                        "syscall\n"
102                        : "=a"(ret)
103                        : "D"(addr), "S"(length), "d"(advice)
104                        : "cc", "rcx", "r11", "memory");
105   return ret;
106 }
107 
108 /* Length of the entries in `struct utsname' is 65.  */
109 #define _UTSNAME_LENGTH 65
110 
111 struct utsname {
112   char sysname[_UTSNAME_LENGTH];  /* Operating system name (e.g., "Linux") */
113   char nodename[_UTSNAME_LENGTH]; /* Name within "some implementation-defined
114                       network" */
115   char release[_UTSNAME_LENGTH]; /* Operating system release (e.g., "2.6.28") */
116   char version[_UTSNAME_LENGTH]; /* Operating system version */
117   char machine[_UTSNAME_LENGTH]; /* Hardware identifier */
118   char domainname[_UTSNAME_LENGTH]; /* NIS or YP domain name */
119 };
120 
121 int __uname(struct utsname *buf) {
122   int ret;
123   __asm__ __volatile__("movq $63, %%rax\n"
124                        "syscall\n"
125                        : "=a"(ret)
126                        : "D"(buf)
127                        : "cc", "rcx", "r11", "memory");
128   return ret;
129 }
130 
131 struct timespec {
132   uint64_t tv_sec;  /* seconds */
133   uint64_t tv_nsec; /* nanoseconds */
134 };
135 
136 uint64_t __nanosleep(const timespec *req, timespec *rem) {
137   uint64_t ret;
138   __asm__ __volatile__("movq $35, %%rax\n"
139                        "syscall\n"
140                        : "=a"(ret)
141                        : "D"(req), "S"(rem)
142                        : "cc", "rcx", "r11", "memory");
143   return ret;
144 }
145 
146 int64_t __fork() {
147   uint64_t ret;
148   __asm__ __volatile__("movq $57, %%rax\n"
149                        "syscall\n"
150                        : "=a"(ret)
151                        :
152                        : "cc", "rcx", "r11", "memory");
153   return ret;
154 }
155 
156 void *__mmap(uint64_t addr, uint64_t size, uint64_t prot, uint64_t flags,
157              uint64_t fd, uint64_t offset) {
158   void *ret;
159   register uint64_t r8 asm("r8") = fd;
160   register uint64_t r9 asm("r9") = offset;
161   register uint64_t r10 asm("r10") = flags;
162   __asm__ __volatile__("movq $9, %%rax\n"
163                        "syscall\n"
164                        : "=a"(ret)
165                        : "D"(addr), "S"(size), "d"(prot), "r"(r10), "r"(r8),
166                          "r"(r9)
167                        : "cc", "rcx", "r11", "memory");
168   return ret;
169 }
170 
171 int __mprotect(void *addr, size_t len, int prot) {
172   int ret;
173   __asm__ __volatile__("movq $10, %%rax\n"
174                        "syscall\n"
175                        : "=a"(ret)
176                        : "D"(addr), "S"(len), "d"(prot)
177                        : "cc", "rcx", "r11", "memory");
178   return ret;
179 }
180 
181 uint64_t __munmap(void *addr, uint64_t size) {
182   uint64_t ret;
183   __asm__ __volatile__("movq $11, %%rax\n"
184                        "syscall\n"
185                        : "=a"(ret)
186                        : "D"(addr), "S"(size)
187                        : "cc", "rcx", "r11", "memory");
188   return ret;
189 }
190 
191 uint64_t __getpid() {
192   uint64_t ret;
193   __asm__ __volatile__("movq $39, %%rax\n"
194                        "syscall\n"
195                        : "=a"(ret)
196                        :
197                        : "cc", "rcx", "r11", "memory");
198   return ret;
199 }
200 
201 uint64_t __getppid() {
202   uint64_t ret;
203   __asm__ __volatile__("movq $110, %%rax\n"
204                        "syscall\n"
205                        : "=a"(ret)
206                        :
207                        : "cc", "rcx", "r11", "memory");
208   return ret;
209 }
210 
211 uint64_t __exit(uint64_t code) {
212   uint64_t ret;
213   __asm__ __volatile__("movq $231, %%rax\n"
214                        "syscall\n"
215                        : "=a"(ret)
216                        : "D"(code)
217                        : "cc", "rcx", "r11", "memory");
218   return ret;
219 }
220 
221 // Helper functions for writing strings to the .fdata file. We intentionally
222 // avoid using libc names (lowercase memset) to make it clear it is our impl.
223 
224 /// Write number Num using Base to the buffer in OutBuf, returns a pointer to
225 /// the end of the string.
226 char *intToStr(char *OutBuf, uint64_t Num, uint32_t Base) {
227   const char *Chars = "0123456789abcdef";
228   char Buf[21];
229   char *Ptr = Buf;
230   while (Num) {
231     *Ptr++ = *(Chars + (Num % Base));
232     Num /= Base;
233   }
234   if (Ptr == Buf) {
235     *OutBuf++ = '0';
236     return OutBuf;
237   }
238   while (Ptr != Buf) {
239     *OutBuf++ = *--Ptr;
240   }
241   return OutBuf;
242 }
243 
244 /// Copy Str to OutBuf, returns a pointer to the end of the copied string
245 char *strCopy(char *OutBuf, const char *Str, int32_t Size = BufSize) {
246   while (*Str) {
247     *OutBuf++ = *Str++;
248     if (--Size <= 0)
249       return OutBuf;
250   }
251   return OutBuf;
252 }
253 
254 void memSet(char *Buf, char C, uint32_t Size) {
255   for (int I = 0; I < Size; ++I)
256     *Buf++ = C;
257 }
258 
259 void *memCpy(void *Dest, const void *Src, size_t Len) {
260   char *d = static_cast<char *>(Dest);
261   const char *s = static_cast<const char *>(Src);
262   while (Len--)
263     *d++ = *s++;
264   return Dest;
265 }
266 
267 uint32_t strLen(const char *Str) {
268   uint32_t Size = 0;
269   while (*Str++)
270     ++Size;
271   return Size;
272 }
273 
274 void reportError(const char *Msg, uint64_t Size) {
275   __write(2, Msg, Size);
276   __exit(1);
277 }
278 
279 void assert(bool Assertion, const char *Msg) {
280   if (Assertion)
281     return;
282   char Buf[BufSize];
283   char *Ptr = Buf;
284   Ptr = strCopy(Ptr, "Assertion failed: ");
285   Ptr = strCopy(Ptr, Msg, BufSize - 40);
286   Ptr = strCopy(Ptr, "\n");
287   reportError(Buf, Ptr - Buf);
288 }
289 
290 void reportNumber(const char *Msg, uint64_t Num, uint32_t Base) {
291   char Buf[BufSize];
292   char *Ptr = Buf;
293   Ptr = strCopy(Ptr, Msg, BufSize - 23);
294   Ptr = intToStr(Ptr, Num, Base);
295   Ptr = strCopy(Ptr, "\n");
296   __write(2, Buf, Ptr - Buf);
297 }
298 
299 void report(const char *Msg) { __write(2, Msg, strLen(Msg)); }
300 
301 /// 1B mutex accessed by lock xchg
302 class Mutex {
303   volatile bool InUse{false};
304 
305 public:
306   bool acquire() {
307     bool Result = true;
308     asm volatile("lock; xchg %0, %1" : "+m"(InUse), "=r"(Result) : : "cc");
309     return !Result;
310   }
311   void release() { InUse = false; }
312 };
313 
314 /// RAII wrapper for Mutex
315 class Lock {
316   Mutex &M;
317 
318 public:
319   Lock(Mutex &M) : M(M) {
320     while (!M.acquire()) {
321     }
322   }
323   ~Lock() { M.release(); }
324 };
325 
326 inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
327   return (Value + Align - 1) / Align * Align;
328 }
329 } // anonymous namespace
330