| File: | bolt/runtime/common.h |
| Warning: | line 209, column 10 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | //===- bolt/runtime/hugify.cpp -------------------------------------------===// | |||
| 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 (__x86_64__1) && !defined(__APPLE__) | |||
| 10 | ||||
| 11 | #include "common.h" | |||
| 12 | ||||
| 13 | #pragma GCC visibility push(hidden) | |||
| 14 | ||||
| 15 | // Enables a very verbose logging to stderr useful when debugging | |||
| 16 | // #define ENABLE_DEBUG | |||
| 17 | ||||
| 18 | #ifdef ENABLE_DEBUG | |||
| 19 | #define DEBUG(X){} \ | |||
| 20 | { X; } | |||
| 21 | #else | |||
| 22 | #define DEBUG(X){} \ | |||
| 23 | {} | |||
| 24 | #endif | |||
| 25 | ||||
| 26 | // Function constains trampoline to _start, | |||
| 27 | // so we can resume regular execution of the function that we hooked. | |||
| 28 | extern void __bolt_hugify_start_program(); | |||
| 29 | ||||
| 30 | // The __hot_start and __hot_end symbols set by Bolt. We use them to figure | |||
| 31 | // out the rage for marking huge pages. | |||
| 32 | extern uint64_t __hot_start; | |||
| 33 | extern uint64_t __hot_end; | |||
| 34 | ||||
| 35 | static void getKernelVersion(uint32_t *Val) { | |||
| 36 | // release should be in the format: %d.%d.%d | |||
| 37 | // major, minor, release | |||
| 38 | struct UtsNameTy UtsName; | |||
| 39 | int Ret = __uname(&UtsName); | |||
| ||||
| 40 | const char *Buf = UtsName.release; | |||
| 41 | const char *End = Buf + strLen(Buf); | |||
| 42 | const char Delims[2][2] = {".", "."}; | |||
| 43 | ||||
| 44 | for (int i = 0; i < 3; ++i) { | |||
| 45 | if (!scanUInt32(Buf, End, Val[i])) { | |||
| 46 | return; | |||
| 47 | } | |||
| 48 | if (i < sizeof(Delims) / sizeof(Delims[0])) { | |||
| 49 | const char *Ptr = Delims[i]; | |||
| 50 | while (*Ptr != '\0') { | |||
| 51 | if (*Ptr != *Buf) { | |||
| 52 | return; | |||
| 53 | } | |||
| 54 | ++Ptr; | |||
| 55 | ++Buf; | |||
| 56 | } | |||
| 57 | } | |||
| 58 | } | |||
| 59 | } | |||
| 60 | ||||
| 61 | /// Check whether the kernel supports THP via corresponding sysfs entry. | |||
| 62 | /// thp works only starting from 5.10 | |||
| 63 | static bool hasPagecacheTHPSupport() { | |||
| 64 | char Buf[64]; | |||
| 65 | ||||
| 66 | int FD = __open("/sys/kernel/mm/transparent_hugepage/enabled", | |||
| 67 | 0 /* O_RDONLY */, 0); | |||
| 68 | if (FD < 0) | |||
| 69 | return false; | |||
| 70 | ||||
| 71 | memset(Buf, 0, sizeof(Buf)); | |||
| 72 | const size_t Res = __read(FD, Buf, sizeof(Buf)); | |||
| 73 | if (Res < 0) | |||
| 74 | return false; | |||
| 75 | ||||
| 76 | if (!strStr(Buf, "[always]") && !strStr(Buf, "[madvise]")) | |||
| 77 | return false; | |||
| 78 | ||||
| 79 | struct KernelVersionTy { | |||
| 80 | uint32_t major; | |||
| 81 | uint32_t minor; | |||
| 82 | uint32_t release; | |||
| 83 | }; | |||
| 84 | ||||
| 85 | KernelVersionTy KernelVersion; | |||
| 86 | ||||
| 87 | getKernelVersion((uint32_t *)&KernelVersion); | |||
| 88 | if (KernelVersion.major >= 5 && KernelVersion.minor >= 10) | |||
| 89 | return true; | |||
| 90 | ||||
| 91 | return false; | |||
| 92 | } | |||
| 93 | ||||
| 94 | static void hugifyForOldKernel(uint8_t *From, uint8_t *To) { | |||
| 95 | const size_t Size = To - From; | |||
| 96 | ||||
| 97 | uint8_t *Mem = reinterpret_cast<uint8_t *>( | |||
| 98 | __mmap(0, Size, 0x3 /* PROT_READ | PROT_WRITE */, | |||
| 99 | 0x22 /* MAP_PRIVATE | MAP_ANONYMOUS */, -1, 0)); | |||
| 100 | ||||
| 101 | if (Mem == ((void *)-1) /* MAP_FAILED */) { | |||
| 102 | char Msg[] = "[hugify] could not allocate memory for text move\n"; | |||
| 103 | reportError(Msg, sizeof(Msg)); | |||
| 104 | } | |||
| 105 | ||||
| 106 | DEBUG(reportNumber("[hugify] allocated temporary address: ", (uint64_t)Mem,{} | |||
| 107 | 16);){} | |||
| 108 | DEBUG(reportNumber("[hugify] allocated size: ", (uint64_t)Size, 16);){} | |||
| 109 | ||||
| 110 | // Copy the hot code to a temporary location. | |||
| 111 | memcpy(Mem, From, Size); | |||
| 112 | ||||
| 113 | __prctl(41 /* PR_SET_THP_DISABLE */, 0, 0, 0, 0); | |||
| 114 | // Maps out the existing hot code. | |||
| 115 | if (__mmap(reinterpret_cast<uint64_t>(From), Size, | |||
| 116 | 0x3 /* PROT_READ | PROT_WRITE */, | |||
| 117 | 0x32 /* MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE */, -1, | |||
| 118 | 0) == ((void *)-1) /*MAP_FAILED*/) { | |||
| 119 | char Msg[] = | |||
| 120 | "[hugify] failed to mmap memory for large page move terminating\n"; | |||
| 121 | reportError(Msg, sizeof(Msg)); | |||
| 122 | } | |||
| 123 | ||||
| 124 | // Mark the hot code page to be huge page. | |||
| 125 | if (__madvise(From, Size, 14 /* MADV_HUGEPAGE */) == -1) { | |||
| 126 | char Msg[] = "[hugify] setting MADV_HUGEPAGE is failed\n"; | |||
| 127 | reportError(Msg, sizeof(Msg)); | |||
| 128 | } | |||
| 129 | ||||
| 130 | // Copy the hot code back. | |||
| 131 | memcpy(From, Mem, Size); | |||
| 132 | ||||
| 133 | // Change permission back to read-only, ignore failure | |||
| 134 | __mprotect(From, Size, 0x5 /* PROT_READ | PROT_EXEC */); | |||
| 135 | ||||
| 136 | __munmap(Mem, Size); | |||
| 137 | } | |||
| 138 | ||||
| 139 | extern "C" void __bolt_hugify_self_impl() { | |||
| 140 | uint8_t *HotStart = (uint8_t *)&__hot_start; | |||
| 141 | uint8_t *HotEnd = (uint8_t *)&__hot_end; | |||
| 142 | // Make sure the start and end are aligned with huge page address | |||
| 143 | const size_t HugePageBytes = 2L * 1024 * 1024; | |||
| 144 | uint8_t *From = HotStart - ((intptr_t)HotStart & (HugePageBytes - 1)); | |||
| 145 | uint8_t *To = HotEnd + (HugePageBytes - 1); | |||
| 146 | To -= (intptr_t)To & (HugePageBytes - 1); | |||
| 147 | ||||
| 148 | DEBUG(reportNumber("[hugify] hot start: ", (uint64_t)HotStart, 16);){} | |||
| 149 | DEBUG(reportNumber("[hugify] hot end: ", (uint64_t)HotEnd, 16);){} | |||
| 150 | DEBUG(reportNumber("[hugify] aligned huge page from: ", (uint64_t)From, 16);){} | |||
| 151 | DEBUG(reportNumber("[hugify] aligned huge page to: ", (uint64_t)To, 16);){} | |||
| 152 | ||||
| 153 | if (!hasPagecacheTHPSupport()) { | |||
| 154 | DEBUG(report({} | |||
| 155 | "[hugify] workaround with memory alignment for kernel < 5.10\n");){} | |||
| 156 | hugifyForOldKernel(From, To); | |||
| 157 | return; | |||
| 158 | } | |||
| 159 | ||||
| 160 | if (__madvise(From, (To - From), 14 /* MADV_HUGEPAGE */) == -1) { | |||
| 161 | char Msg[] = "[hugify] failed to allocate large page\n"; | |||
| 162 | // TODO: allow user to control the failure behavior. | |||
| 163 | reportError(Msg, sizeof(Msg)); | |||
| 164 | } | |||
| 165 | } | |||
| 166 | ||||
| 167 | /// This is hooking ELF's entry, it needs to save all machine state. | |||
| 168 | extern "C" __attribute((naked)) void __bolt_hugify_self() { | |||
| 169 | #if defined(__x86_64__1) | |||
| 170 | __asm__ __volatile__(SAVE_ALL"push %%rax\n" "push %%rbx\n" "push %%rcx\n" "push %%rdx\n" "push %%rdi\n" "push %%rsi\n" "push %%rbp\n" "push %%r8\n" "push %%r9\n" "push %%r10\n" "push %%r11\n" "push %%r12\n" "push %%r13\n" "push %%r14\n" "push %%r15\n" "sub $8, %%rsp\n" "call __bolt_hugify_self_impl\n" RESTORE_ALL"add $8, %%rsp\n" "pop %%r15\n" "pop %%r14\n" "pop %%r13\n" "pop %%r12\n" "pop %%r11\n" "pop %%r10\n" "pop %%r9\n" "pop %%r8\n" "pop %%rbp\n" "pop %%rsi\n" "pop %%rdi\n" "pop %%rdx\n" "pop %%rcx\n" "pop %%rbx\n" "pop %%rax\n" | |||
| 171 | "jmp __bolt_hugify_start_program\n" :: | |||
| 172 | :); | |||
| 173 | #else | |||
| 174 | exit(1); | |||
| 175 | #endif | |||
| 176 | } | |||
| 177 | #endif |
| 1 | #ifndef LLVM_TOOLS_LLVM_BOLT_SYS_X86_64 |
| 2 | #define LLVM_TOOLS_LLVM_BOLT_SYS_X86_64 |
| 3 | |
| 4 | // Save all registers while keeping 16B stack alignment |
| 5 | #define SAVE_ALL"push %%rax\n" "push %%rbx\n" "push %%rcx\n" "push %%rdx\n" "push %%rdi\n" "push %%rsi\n" "push %%rbp\n" "push %%r8\n" "push %%r9\n" "push %%r10\n" "push %%r11\n" "push %%r12\n" "push %%r13\n" "push %%r14\n" "push %%r15\n" "sub $8, %%rsp\n" \ |
| 6 | "push %%rax\n" \ |
| 7 | "push %%rbx\n" \ |
| 8 | "push %%rcx\n" \ |
| 9 | "push %%rdx\n" \ |
| 10 | "push %%rdi\n" \ |
| 11 | "push %%rsi\n" \ |
| 12 | "push %%rbp\n" \ |
| 13 | "push %%r8\n" \ |
| 14 | "push %%r9\n" \ |
| 15 | "push %%r10\n" \ |
| 16 | "push %%r11\n" \ |
| 17 | "push %%r12\n" \ |
| 18 | "push %%r13\n" \ |
| 19 | "push %%r14\n" \ |
| 20 | "push %%r15\n" \ |
| 21 | "sub $8, %%rsp\n" |
| 22 | // Mirrors SAVE_ALL |
| 23 | #define RESTORE_ALL"add $8, %%rsp\n" "pop %%r15\n" "pop %%r14\n" "pop %%r13\n" "pop %%r12\n" "pop %%r11\n" "pop %%r10\n" "pop %%r9\n" "pop %%r8\n" "pop %%rbp\n" "pop %%rsi\n" "pop %%rdi\n" "pop %%rdx\n" "pop %%rcx\n" "pop %%rbx\n" "pop %%rax\n" \ |
| 24 | "add $8, %%rsp\n" \ |
| 25 | "pop %%r15\n" \ |
| 26 | "pop %%r14\n" \ |
| 27 | "pop %%r13\n" \ |
| 28 | "pop %%r12\n" \ |
| 29 | "pop %%r11\n" \ |
| 30 | "pop %%r10\n" \ |
| 31 | "pop %%r9\n" \ |
| 32 | "pop %%r8\n" \ |
| 33 | "pop %%rbp\n" \ |
| 34 | "pop %%rsi\n" \ |
| 35 | "pop %%rdi\n" \ |
| 36 | "pop %%rdx\n" \ |
| 37 | "pop %%rcx\n" \ |
| 38 | "pop %%rbx\n" \ |
| 39 | "pop %%rax\n" |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | // Get the difference between runtime addrress of .text section and |
| 44 | // static address in section header table. Can be extracted from arbitrary |
| 45 | // pc value recorded at runtime to get the corresponding static address, which |
| 46 | // in turn can be used to search for indirect call description. Needed because |
| 47 | // indirect call descriptions are read-only non-relocatable data. |
| 48 | uint64_t getTextBaseAddress() { |
| 49 | uint64_t DynAddr; |
| 50 | uint64_t StaticAddr; |
| 51 | __asm__ volatile("leaq __hot_end(%%rip), %0\n\t" |
| 52 | "movabsq $__hot_end, %1\n\t" |
| 53 | : "=r"(DynAddr), "=r"(StaticAddr)); |
| 54 | return DynAddr - StaticAddr; |
| 55 | } |
| 56 | |
| 57 | #define _STRINGIFY(x)"x" #x |
| 58 | #define STRINGIFY(x)"x" _STRINGIFY(x)"x" |
| 59 | |
| 60 | uint64_t __read(uint64_t fd, const void *buf, uint64_t count) { |
| 61 | uint64_t ret; |
| 62 | #if defined(__APPLE__) |
| 63 | #define READ_SYSCALL0 0x2000003 |
| 64 | #else |
| 65 | #define READ_SYSCALL0 0 |
| 66 | #endif |
| 67 | __asm__ __volatile__("movq $" STRINGIFY(READ_SYSCALL)"0" ", %%rax\n" |
| 68 | "syscall\n" |
| 69 | : "=a"(ret) |
| 70 | : "D"(fd), "S"(buf), "d"(count) |
| 71 | : "cc", "rcx", "r11", "memory"); |
| 72 | return ret; |
| 73 | } |
| 74 | |
| 75 | uint64_t __write(uint64_t fd, const void *buf, uint64_t count) { |
| 76 | uint64_t ret; |
| 77 | #if defined(__APPLE__) |
| 78 | #define WRITE_SYSCALL1 0x2000004 |
| 79 | #else |
| 80 | #define WRITE_SYSCALL1 1 |
| 81 | #endif |
| 82 | __asm__ __volatile__("movq $" STRINGIFY(WRITE_SYSCALL)"1" ", %%rax\n" |
| 83 | "syscall\n" |
| 84 | : "=a"(ret) |
| 85 | : "D"(fd), "S"(buf), "d"(count) |
| 86 | : "cc", "rcx", "r11", "memory"); |
| 87 | return ret; |
| 88 | } |
| 89 | |
| 90 | void *__mmap(uint64_t addr, uint64_t size, uint64_t prot, uint64_t flags, |
| 91 | uint64_t fd, uint64_t offset) { |
| 92 | #if defined(__APPLE__) |
| 93 | #define MMAP_SYSCALL9 0x20000c5 |
| 94 | #else |
| 95 | #define MMAP_SYSCALL9 9 |
| 96 | #endif |
| 97 | void *ret; |
| 98 | register uint64_t r8 asm("r8") = fd; |
| 99 | register uint64_t r9 asm("r9") = offset; |
| 100 | register uint64_t r10 asm("r10") = flags; |
| 101 | __asm__ __volatile__("movq $" STRINGIFY(MMAP_SYSCALL)"9" ", %%rax\n" |
| 102 | "syscall\n" |
| 103 | : "=a"(ret) |
| 104 | : "D"(addr), "S"(size), "d"(prot), "r"(r10), "r"(r8), |
| 105 | "r"(r9) |
| 106 | : "cc", "rcx", "r11", "memory"); |
| 107 | return ret; |
| 108 | } |
| 109 | |
| 110 | uint64_t __munmap(void *addr, uint64_t size) { |
| 111 | #if defined(__APPLE__) |
| 112 | #define MUNMAP_SYSCALL11 0x2000049 |
| 113 | #else |
| 114 | #define MUNMAP_SYSCALL11 11 |
| 115 | #endif |
| 116 | uint64_t ret; |
| 117 | __asm__ __volatile__("movq $" STRINGIFY(MUNMAP_SYSCALL)"11" ", %%rax\n" |
| 118 | "syscall\n" |
| 119 | : "=a"(ret) |
| 120 | : "D"(addr), "S"(size) |
| 121 | : "cc", "rcx", "r11", "memory"); |
| 122 | return ret; |
| 123 | } |
| 124 | |
| 125 | uint64_t __sigprocmask(int how, const void *set, void *oldset) { |
| 126 | #if defined(__APPLE__) |
| 127 | #define SIGPROCMASK_SYSCALL14 0x2000030 |
| 128 | #else |
| 129 | #define SIGPROCMASK_SYSCALL14 14 |
| 130 | #endif |
| 131 | uint64_t ret; |
| 132 | register long r10 asm("r10") = sizeof(uint64_t); |
| 133 | __asm__ __volatile__("movq $" STRINGIFY(SIGPROCMASK_SYSCALL)"14" ", %%rax\n" |
| 134 | "syscall\n" |
| 135 | : "=a"(ret) |
| 136 | : "D"(how), "S"(set), "d"(oldset), "r"(r10) |
| 137 | : "cc", "rcx", "r11", "memory"); |
| 138 | return ret; |
| 139 | } |
| 140 | |
| 141 | uint64_t __getpid() { |
| 142 | uint64_t ret; |
| 143 | #if defined(__APPLE__) |
| 144 | #define GETPID_SYSCALL39 20 |
| 145 | #else |
| 146 | #define GETPID_SYSCALL39 39 |
| 147 | #endif |
| 148 | __asm__ __volatile__("movq $" STRINGIFY(GETPID_SYSCALL)"39" ", %%rax\n" |
| 149 | "syscall\n" |
| 150 | : "=a"(ret) |
| 151 | : |
| 152 | : "cc", "rcx", "r11", "memory"); |
| 153 | return ret; |
| 154 | } |
| 155 | |
| 156 | uint64_t __exit(uint64_t code) { |
| 157 | #if defined(__APPLE__) |
| 158 | #define EXIT_SYSCALL231 0x2000001 |
| 159 | #else |
| 160 | #define EXIT_SYSCALL231 231 |
| 161 | #endif |
| 162 | uint64_t ret; |
| 163 | __asm__ __volatile__("movq $" STRINGIFY(EXIT_SYSCALL)"231" ", %%rax\n" |
| 164 | "syscall\n" |
| 165 | : "=a"(ret) |
| 166 | : "D"(code) |
| 167 | : "cc", "rcx", "r11", "memory"); |
| 168 | return ret; |
| 169 | } |
| 170 | |
| 171 | #if !defined(__APPLE__) |
| 172 | // We use a stack-allocated buffer for string manipulation in many pieces of |
| 173 | // this code, including the code that prints each line of the fdata file. This |
| 174 | // buffer needs to accomodate large function names, but shouldn't be arbitrarily |
| 175 | // large (dynamically allocated) for simplicity of our memory space usage. |
| 176 | |
| 177 | // Declare some syscall wrappers we use throughout this code to avoid linking |
| 178 | // against system libc. |
| 179 | uint64_t __open(const char *pathname, uint64_t flags, uint64_t mode) { |
| 180 | uint64_t ret; |
| 181 | __asm__ __volatile__("movq $2, %%rax\n" |
| 182 | "syscall" |
| 183 | : "=a"(ret) |
| 184 | : "D"(pathname), "S"(flags), "d"(mode) |
| 185 | : "cc", "rcx", "r11", "memory"); |
| 186 | return ret; |
| 187 | } |
| 188 | |
| 189 | long __getdents64(unsigned int fd, dirent64 *dirp, size_t count) { |
| 190 | long ret; |
| 191 | __asm__ __volatile__("movq $217, %%rax\n" |
| 192 | "syscall" |
| 193 | : "=a"(ret) |
| 194 | : "D"(fd), "S"(dirp), "d"(count) |
| 195 | : "cc", "rcx", "r11", "memory"); |
| 196 | return ret; |
| 197 | } |
| 198 | |
| 199 | uint64_t __readlink(const char *pathname, char *buf, size_t bufsize) { |
| 200 | uint64_t ret; |
| 201 | __asm__ __volatile__("movq $89, %%rax\n" |
| 202 | "syscall" |
| 203 | : "=a"(ret) |
| 204 | : "D"(pathname), "S"(buf), "d"(bufsize) |
| 205 | : "cc", "rcx", "r11", "memory"); |
| 206 | return ret; |
| 207 | } |
| 208 | |
| 209 | uint64_t __lseek(uint64_t fd, uint64_t pos, uint64_t whence) { |
| 210 | uint64_t ret; |
| 211 | __asm__ __volatile__("movq $8, %%rax\n" |
| 212 | "syscall\n" |
| 213 | : "=a"(ret) |
| 214 | : "D"(fd), "S"(pos), "d"(whence) |
| 215 | : "cc", "rcx", "r11", "memory"); |
| 216 | return ret; |
| 217 | } |
| 218 | |
| 219 | int __ftruncate(uint64_t fd, uint64_t length) { |
| 220 | int ret; |
| 221 | __asm__ __volatile__("movq $77, %%rax\n" |
| 222 | "syscall\n" |
| 223 | : "=a"(ret) |
| 224 | : "D"(fd), "S"(length) |
| 225 | : "cc", "rcx", "r11", "memory"); |
| 226 | return ret; |
| 227 | } |
| 228 | |
| 229 | int __close(uint64_t fd) { |
| 230 | uint64_t ret; |
| 231 | __asm__ __volatile__("movq $3, %%rax\n" |
| 232 | "syscall\n" |
| 233 | : "=a"(ret) |
| 234 | : "D"(fd) |
| 235 | : "cc", "rcx", "r11", "memory"); |
| 236 | return ret; |
| 237 | } |
| 238 | |
| 239 | int __madvise(void *addr, size_t length, int advice) { |
| 240 | int ret; |
| 241 | __asm__ __volatile__("movq $28, %%rax\n" |
| 242 | "syscall\n" |
| 243 | : "=a"(ret) |
| 244 | : "D"(addr), "S"(length), "d"(advice) |
| 245 | : "cc", "rcx", "r11", "memory"); |
| 246 | return ret; |
| 247 | } |
| 248 | |
| 249 | int __uname(struct UtsNameTy *Buf) { |
| 250 | int Ret; |
| 251 | __asm__ __volatile__("movq $63, %%rax\n" |
| 252 | "syscall\n" |
| 253 | : "=a"(Ret) |
| 254 | : "D"(Buf) |
| 255 | : "cc", "rcx", "r11", "memory"); |
| 256 | return Ret; |
| 257 | } |
| 258 | |
| 259 | uint64_t __nanosleep(const timespec *req, timespec *rem) { |
| 260 | uint64_t ret; |
| 261 | __asm__ __volatile__("movq $35, %%rax\n" |
| 262 | "syscall\n" |
| 263 | : "=a"(ret) |
| 264 | : "D"(req), "S"(rem) |
| 265 | : "cc", "rcx", "r11", "memory"); |
| 266 | return ret; |
| 267 | } |
| 268 | |
| 269 | int64_t __fork() { |
| 270 | uint64_t ret; |
| 271 | __asm__ __volatile__("movq $57, %%rax\n" |
| 272 | "syscall\n" |
| 273 | : "=a"(ret) |
| 274 | : |
| 275 | : "cc", "rcx", "r11", "memory"); |
| 276 | return ret; |
| 277 | } |
| 278 | |
| 279 | int __mprotect(void *addr, size_t len, int prot) { |
| 280 | int ret; |
| 281 | __asm__ __volatile__("movq $10, %%rax\n" |
| 282 | "syscall\n" |
| 283 | : "=a"(ret) |
| 284 | : "D"(addr), "S"(len), "d"(prot) |
| 285 | : "cc", "rcx", "r11", "memory"); |
| 286 | return ret; |
| 287 | } |
| 288 | |
| 289 | uint64_t __getppid() { |
| 290 | uint64_t ret; |
| 291 | __asm__ __volatile__("movq $110, %%rax\n" |
| 292 | "syscall\n" |
| 293 | : "=a"(ret) |
| 294 | : |
| 295 | : "cc", "rcx", "r11", "memory"); |
| 296 | return ret; |
| 297 | } |
| 298 | |
| 299 | int __setpgid(uint64_t pid, uint64_t pgid) { |
| 300 | int ret; |
| 301 | __asm__ __volatile__("movq $109, %%rax\n" |
| 302 | "syscall\n" |
| 303 | : "=a"(ret) |
| 304 | : "D"(pid), "S"(pgid) |
| 305 | : "cc", "rcx", "r11", "memory"); |
| 306 | return ret; |
| 307 | } |
| 308 | |
| 309 | uint64_t __getpgid(uint64_t pid) { |
| 310 | uint64_t ret; |
| 311 | __asm__ __volatile__("movq $121, %%rax\n" |
| 312 | "syscall\n" |
| 313 | : "=a"(ret) |
| 314 | : "D"(pid) |
| 315 | : "cc", "rcx", "r11", "memory"); |
| 316 | return ret; |
| 317 | } |
| 318 | |
| 319 | int __kill(uint64_t pid, int sig) { |
| 320 | int ret; |
| 321 | __asm__ __volatile__("movq $62, %%rax\n" |
| 322 | "syscall\n" |
| 323 | : "=a"(ret) |
| 324 | : "D"(pid), "S"(sig) |
| 325 | : "cc", "rcx", "r11", "memory"); |
| 326 | return ret; |
| 327 | } |
| 328 | |
| 329 | int __fsync(int fd) { |
| 330 | int ret; |
| 331 | __asm__ __volatile__("movq $74, %%rax\n" |
| 332 | "syscall\n" |
| 333 | : "=a"(ret) |
| 334 | : "D"(fd) |
| 335 | : "cc", "rcx", "r11", "memory"); |
| 336 | return ret; |
| 337 | } |
| 338 | |
| 339 | // %rdi %rsi %rdx %r10 %r8 |
| 340 | // sys_prctl int option unsigned unsigned unsigned unsigned |
| 341 | // long arg2 long arg3 long arg4 long arg5 |
| 342 | int __prctl(int Option, unsigned long Arg2, unsigned long Arg3, |
| 343 | unsigned long Arg4, unsigned long Arg5) { |
| 344 | int Ret; |
| 345 | register long rdx asm("rdx") = Arg3; |
| 346 | register long r8 asm("r8") = Arg5; |
| 347 | register long r10 asm("r10") = Arg4; |
| 348 | __asm__ __volatile__("movq $157, %%rax\n" |
| 349 | "syscall\n" |
| 350 | : "=a"(Ret) |
| 351 | : "D"(Option), "S"(Arg2), "d"(rdx), "r"(r10), "r"(r8) |
| 352 | :); |
| 353 | return Ret; |
| 354 | } |
| 355 | |
| 356 | #endif |
| 357 | |
| 358 | } // anonymous namespace |
| 359 | |
| 360 | #endif |
| 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__1) | |||
| 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__long unsigned int size_t; | |||
| 23 | #define __SSIZE_TYPE__ \ | |||
| 24 | __typeof__(_Generic((__SIZE_TYPE__long unsigned int)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_READ0x1 0x1 /* Page can be read. */ | |||
| 44 | #define PROT_WRITE0x2 0x2 /* Page can be written. */ | |||
| 45 | #define PROT_EXEC0x4 0x4 /* Page can be executed. */ | |||
| 46 | #define PROT_NONE0x0 0x0 /* Page can not be accessed. */ | |||
| 47 | #define PROT_GROWSDOWN0x01000000 \ | |||
| 48 | 0x01000000 /* Extend change to start of \ | |||
| 49 | growsdown vma (mprotect only). */ | |||
| 50 | #define PROT_GROWSUP0x02000000 \ | |||
| 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_SHARED0x01 0x01 /* Share changes. */ | |||
| 56 | #define MAP_PRIVATE0x02 0x02 /* Changes are private. */ | |||
| 57 | #define MAP_FIXED0x10 0x10 /* Interpret addr exactly. */ | |||
| 58 | ||||
| 59 | #if defined(__APPLE__) | |||
| 60 | #define MAP_ANONYMOUS0x20 0x1000 | |||
| 61 | #else | |||
| 62 | #define MAP_ANONYMOUS0x20 0x20 | |||
| 63 | #endif | |||
| 64 | ||||
| 65 | #define MAP_FAILED((void *)-1) ((void *)-1) | |||
| 66 | ||||
| 67 | #define SEEK_SET0 0 /* Seek from beginning of file. */ | |||
| 68 | #define SEEK_CUR1 1 /* Seek from current position. */ | |||
| 69 | #define SEEK_END2 2 /* Seek from end of file. */ | |||
| 70 | ||||
| 71 | #define O_RDONLY0 0 | |||
| 72 | #define O_WRONLY1 1 | |||
| 73 | #define O_RDWR2 2 | |||
| 74 | #define O_CREAT64 64 | |||
| 75 | #define O_TRUNC512 512 | |||
| 76 | #define O_APPEND1024 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_LENGTH65 65 | |||
| 138 | ||||
| 139 | struct UtsNameTy { | |||
| 140 | char sysname[_UTSNAME_LENGTH65]; /* Operating system name (e.g., "Linux") */ | |||
| 141 | char nodename[_UTSNAME_LENGTH65]; /* Name within "some implementation-defined | |||
| 142 | network" */ | |||
| 143 | char release[_UTSNAME_LENGTH65]; /* Operating system release (e.g., "2.6.28") */ | |||
| 144 | char version[_UTSNAME_LENGTH65]; /* Operating system version */ | |||
| 145 | char machine[_UTSNAME_LENGTH65]; /* Hardware identifier */ | |||
| 146 | char domainname[_UTSNAME_LENGTH65]; /* 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(Haystack); i++) { | |||
| 218 | if (Haystack[i] == Needle[0]) { | |||
| 219 | for (j = 1; j < strLen(Needle); j++) { | |||
| 220 | if (Haystack[i + j] != Needle[j]) | |||
| 221 | break; | |||
| 222 | } | |||
| 223 | if (j == strLen(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(Ptr, Msg, BufSize - 23); | |||
| 234 | Ptr = intToStr(Ptr, Num, Base); | |||
| 235 | Ptr = strCopy(Ptr, "\n"); | |||
| 236 | __write(2, Buf, Ptr - Buf); | |||
| 237 | } | |||
| 238 | ||||
| 239 | void report(const char *Msg) { __write(2, Msg, strLen(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(2, Msg, Size); | |||
| 278 | __exit(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(Ptr, "Assertion failed: "); | |||
| 287 | Ptr = strCopy(Ptr, Msg, BufSize - 40); | |||
| 288 | Ptr = strCopy(Ptr, "\n"); | |||
| 289 | reportError(Buf, Ptr - Buf); | |||
| 290 | } | |||
| 291 | ||||
| 292 | #define SIG_BLOCK0 0 | |||
| 293 | #define SIG_UNBLOCK1 1 | |||
| 294 | #define SIG_SETMASK2 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_ACQUIRE2); } | |||
| 303 | void release() { __atomic_clear(&InUse, __ATOMIC_RELEASE3); } | |||
| 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_BLOCK0, MaskAllSignals, SignalMask); | |||
| 314 | while (!M.acquire()) { | |||
| 315 | } | |||
| 316 | } | |||
| 317 | ||||
| 318 | ~Lock() { | |||
| 319 | M.release(); | |||
| 320 | __sigprocmask(SIG_SETMASK2, SignalMask, 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 |