| //===- bolt/runtime/common.h ------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #if defined(__linux__) | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include "config.h" | |
| #ifdef HAVE_ELF_H | |
| #include <elf.h> | |
| #endif | |
| #elif defined(__APPLE__) | |
| typedef __SIZE_TYPE__ size_t; | |
| #define __SSIZE_TYPE__ \ | |
| __typeof__(_Generic((__SIZE_TYPE__)0, unsigned long long int \ | |
| : (long long int)0, unsigned long int \ | |
| : (long int)0, unsigned int \ | |
| : (int)0, unsigned short \ | |
| : (short)0, unsigned char \ | |
| : (signed char)0)) | |
| typedef __SSIZE_TYPE__ ssize_t; | |
| typedef unsigned long long uint64_t; | |
| typedef unsigned uint32_t; | |
| typedef unsigned char uint8_t; | |
| typedef long long int64_t; | |
| typedef int int32_t; | |
| #else | |
| #error "For Linux or MacOS only" | |
| #endif | |
| #define PROT_READ 0x1 /* Page can be read. */ | |
| #define PROT_WRITE 0x2 /* Page can be written. */ | |
| #define PROT_EXEC 0x4 /* Page can be executed. */ | |
| #define PROT_NONE 0x0 /* Page can not be accessed. */ | |
| #define PROT_GROWSDOWN \ | |
| 0x01000000 /* Extend change to start of \ | |
| growsdown vma (mprotect only). */ | |
| #define PROT_GROWSUP \ | |
| 0x02000000 /* Extend change to start of \ | |
| growsup vma (mprotect only). */ | |
| /* Sharing types (must choose one and only one of these). */ | |
| #define MAP_SHARED 0x01 /* Share changes. */ | |
| #define MAP_PRIVATE 0x02 /* Changes are private. */ | |
| #define MAP_FIXED 0x10 /* Interpret addr exactly. */ | |
| #if defined(__APPLE__) | |
| #define MAP_ANONYMOUS 0x1000 | |
| #else | |
| #define MAP_ANONYMOUS 0x20 | |
| #endif | |
| #define MAP_FAILED ((void *)-1) | |
| #define SEEK_SET 0 /* Seek from beginning of file. */ | |
| #define SEEK_CUR 1 /* Seek from current position. */ | |
| #define SEEK_END 2 /* Seek from end of file. */ | |
| #define O_RDONLY 0 | |
| #define O_WRONLY 1 | |
| #define O_RDWR 2 | |
| #define O_CREAT 64 | |
| #define O_TRUNC 512 | |
| #define O_APPEND 1024 | |
| #define O_CLOEXEC 524288 | |
| // Functions that are required by freestanding environment. Compiler may | |
| // generate calls to these implicitly. | |
| extern "C" { | |
| void *memcpy(void *Dest, const void *Src, size_t Len) { | |
| uint8_t *d = static_cast<uint8_t *>(Dest); | |
| const uint8_t *s = static_cast<const uint8_t *>(Src); | |
| while (Len--) | |
| *d++ = *s++; | |
| return Dest; | |
| } | |
| void *memmove(void *Dest, const void *Src, size_t Len) { | |
| uint8_t *d = static_cast<uint8_t *>(Dest); | |
| const uint8_t *s = static_cast<const uint8_t *>(Src); | |
| if (d < s) { | |
| while (Len--) | |
| *d++ = *s++; | |
| } else { | |
| s += Len - 1; | |
| d += Len - 1; | |
| while (Len--) | |
| *d-- = *s--; | |
| } | |
| return Dest; | |
| } | |
| void *memset(void *Buf, int C, size_t Size) { | |
| char *S = (char *)Buf; | |
| for (size_t I = 0; I < Size; ++I) | |
| *S++ = C; | |
| return Buf; | |
| } | |
| int memcmp(const void *s1, const void *s2, size_t n) { | |
| const uint8_t *c1 = static_cast<const uint8_t *>(s1); | |
| const uint8_t *c2 = static_cast<const uint8_t *>(s2); | |
| for (; n--; c1++, c2++) { | |
| if (*c1 != *c2) | |
| return *c1 < *c2 ? -1 : 1; | |
| } | |
| return 0; | |
| } | |
| } // extern "C" | |
| // Anonymous namespace covering everything but our library entry point | |
| namespace { | |
| struct dirent64 { | |
| uint64_t d_ino; /* Inode number */ | |
| int64_t d_off; /* Offset to next linux_dirent */ | |
| unsigned short d_reclen; /* Length of this linux_dirent */ | |
| unsigned char d_type; | |
| char d_name[]; /* Filename (null-terminated) */ | |
| /* length is actually (d_reclen - 2 - | |
| offsetof(struct linux_dirent, d_name)) */ | |
| }; | |
| /* Length of the entries in `struct utsname' is 65. */ | |
| #define _UTSNAME_LENGTH 65 | |
| struct UtsNameTy { | |
| char sysname[_UTSNAME_LENGTH]; /* Operating system name (e.g., "Linux") */ | |
| char nodename[_UTSNAME_LENGTH]; /* Name within "some implementation-defined | |
| network" */ | |
| char release[_UTSNAME_LENGTH]; /* Operating system release (e.g., "2.6.28") */ | |
| char version[_UTSNAME_LENGTH]; /* Operating system version */ | |
| char machine[_UTSNAME_LENGTH]; /* Hardware identifier */ | |
| char domainname[_UTSNAME_LENGTH]; /* NIS or YP domain name */ | |
| }; | |
| struct timespec { | |
| uint64_t tv_sec; /* seconds */ | |
| uint64_t tv_nsec; /* nanoseconds */ | |
| }; | |
| #if defined(__aarch64__) || defined(__arm64__) | |
| #if defined(__ANDROID__) | |
| #define ANDROID_AARCH64 | |
| #endif | |
| #include "sys_aarch64.h" | |
| #elif defined(__riscv) | |
| #include "sys_riscv64.h" | |
| #elif defined(__x86_64__) | |
| #include "sys_x86_64.h" | |
| #else | |
| #error "For AArch64/ARM64,X86_64 AND RISCV64 only." | |
| #endif | |
| constexpr uint32_t BufSize = 32768U; | |
| // Helper functions for writing strings to the .fdata file. We intentionally | |
| // avoid using libc names to make it clear it is our impl. | |
| /// Write number Num using Base to the buffer in OutBuf, returns a pointer to | |
| /// the end of the string. | |
| char *intToStr(char *OutBuf, uint64_t Num, uint32_t Base) { | |
| const char *Chars = "0123456789abcdef"; | |
| char Buf[21]; | |
| char *Ptr = Buf; | |
| while (Num) { | |
| *Ptr++ = *(Chars + (Num % Base)); | |
| Num /= Base; | |
| } | |
| if (Ptr == Buf) { | |
| *OutBuf++ = '0'; | |
| return OutBuf; | |
| } | |
| while (Ptr != Buf) | |
| *OutBuf++ = *--Ptr; | |
| return OutBuf; | |
| } | |
| /// Copy Str to OutBuf, returns a pointer to the end of the copied string | |
| char *strCopy(char *OutBuf, const char *Str, int32_t Size = BufSize) { | |
| while (*Str) { | |
| *OutBuf++ = *Str++; | |
| if (--Size <= 0) | |
| return OutBuf; | |
| } | |
| return OutBuf; | |
| } | |
| /// Compare two strings, at most Num bytes. | |
| int strnCmp(const char *Str1, const char *Str2, size_t Num) { | |
| while (Num && *Str1 && (*Str1 == *Str2)) { | |
| Num--; | |
| Str1++; | |
| Str2++; | |
| } | |
| if (Num == 0) | |
| return 0; | |
| return *(unsigned char *)Str1 - *(unsigned char *)Str2; | |
| } | |
| uint32_t strLen(const char *Str) { | |
| uint32_t Size = 0; | |
| while (*Str++) | |
| ++Size; | |
| return Size; | |
| } | |
| void *strStr(const char *const Haystack, const char *const Needle) { | |
| int j = 0; | |
| for (int i = 0; i < strLen(Haystack); i++) { | |
| if (Haystack[i] == Needle[0]) { | |
| for (j = 1; j < strLen(Needle); j++) { | |
| if (Haystack[i + j] != Needle[j]) | |
| break; | |
| } | |
| if (j == strLen(Needle)) | |
| return (void *)&Haystack[i]; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| // Diagnostic helpers allocate a large on-stack scratch buffer. Keep them | |
| // out of line: if inlined, the buffer is reserved in every caller frame and | |
| // can overflow small thread stacks even when the report is never emitted. | |
| __attribute__((noinline)) void reportNumber(const char *Msg, uint64_t Num, | |
| uint32_t Base) { | |
| #if !defined(ANDROID_AARCH64) | |
| char Buf[BufSize]; | |
| char *Ptr = Buf; | |
| Ptr = strCopy(Ptr, Msg, BufSize - 23); | |
| Ptr = intToStr(Ptr, Num, Base); | |
| Ptr = strCopy(Ptr, "\n"); | |
| __write(2, Buf, Ptr - Buf); | |
| #endif | |
| } | |
| void report(const char *Msg) { | |
| #if !defined(ANDROID_AARCH64) | |
| __write(2, Msg, strLen(Msg)); | |
| #endif | |
| } | |
| unsigned long hexToLong(const char *Str, char Terminator = '\0') { | |
| unsigned long Res = 0; | |
| while (*Str != Terminator) { | |
| Res <<= 4; | |
| if ('0' <= *Str && *Str <= '9') | |
| Res += *Str++ - '0'; | |
| else if ('a' <= *Str && *Str <= 'f') | |
| Res += *Str++ - 'a' + 10; | |
| else if ('A' <= *Str && *Str <= 'F') | |
| Res += *Str++ - 'A' + 10; | |
| else | |
| return 0; | |
| } | |
| return Res; | |
| } | |
| /// Starting from character at \p buf, find the longest consecutive sequence | |
| /// of digits (0-9) and convert it to uint32_t. The converted value | |
| /// is put into \p ret. \p end marks the end of the buffer to avoid buffer | |
| /// overflow. The function \returns whether a valid uint32_t value is found. | |
| /// \p buf will be updated to the next character right after the digits. | |
| static bool scanUInt32(const char *&Buf, const char *End, uint32_t &Ret) { | |
| uint64_t Result = 0; | |
| const char *OldBuf = Buf; | |
| while (Buf < End && ((*Buf) >= '0' && (*Buf) <= '9')) { | |
| Result = Result * 10 + (*Buf) - '0'; | |
| ++Buf; | |
| } | |
| if (OldBuf != Buf && Result <= 0xFFFFFFFFu) { | |
| Ret = static_cast<uint32_t>(Result); | |
| return true; | |
| } | |
| return false; | |
| } | |
| #if defined(ANDROID_AARCH64) | |
| // On Android the BOLT instrumentation runtime is embedded in a shipping app | |
| // process. Any failure in runtime should not terminate the host process with | |
| // __exit(1), which would turn a profiling-tool defect into a foreground app | |
| // crash. Instead, __bolt_instr_data_dump() installs a setjmp() recovery point | |
| // and a failed assert() or reportError() on the dumping thread will record | |
| // the failure and then longjmp() back reporting failure to the caller. | |
| alignas(16) void *__bolt_instr_longjmp_buf[16]; | |
| // Thread that installed the recovery point above, or 0 when there is none. | |
| uint64_t __bolt_instr_recovery_tid = 0; | |
| // Set the first time any runtime operation fails and will never be cleared. | |
| // Once set, every instrumentation entry point (setup, indirect-call handler, | |
| // profile dumping, counter clearing) turns into a no-op, so any profiling | |
| // related runtime error just fails profiling but won't kill the host app. | |
| bool __bolt_runtime_error = false; | |
| void boltHandleFatalAndRecover() { | |
| __atomic_store_n(&__bolt_runtime_error, true, __ATOMIC_RELAXED); | |
| if (__atomic_load_n(&__bolt_instr_recovery_tid, __ATOMIC_RELAXED) == | |
| __gettid()) { | |
| __atomic_store_n(&__bolt_instr_recovery_tid, 0, __ATOMIC_RELAXED); | |
| __bolt_longjmp(__bolt_instr_longjmp_buf, 1); | |
| } | |
| } | |
| #endif // defined(ANDROID_AARCH64) | |
| void reportError(const char *Msg, uint64_t Size) { | |
| #if defined(ANDROID_AARCH64) | |
| (void)Msg; | |
| (void)Size; | |
| boltHandleFatalAndRecover(); | |
| #else | |
| __write(2, Msg, Size); | |
| __exit(1); | |
| #endif | |
| } | |
| // Failure path of assert() kept out of line so its large on-stack buffer is | |
| // not reserved in every inlined caller frame (see reportNumber above). | |
| __attribute__((noinline)) void reportAssertFailure(const char *Msg) { | |
| #if defined(ANDROID_AARCH64) | |
| (void)Msg; | |
| boltHandleFatalAndRecover(); | |
| #else | |
| char Buf[BufSize]; | |
| char *Ptr = Buf; | |
| Ptr = strCopy(Ptr, "Assertion failed: "); | |
| Ptr = strCopy(Ptr, Msg, BufSize - 40); | |
| Ptr = strCopy(Ptr, "\n"); | |
| reportError(Buf, Ptr - Buf); | |
| #endif | |
| } | |
| void assert(bool Assertion, const char *Msg) { | |
| if (!Assertion) | |
| reportAssertFailure(Msg); | |
| } | |
| #define SIG_BLOCK 0 | |
| #define SIG_UNBLOCK 1 | |
| #define SIG_SETMASK 2 | |
| static const uint64_t MaskAllSignals[] = {-1ULL}; | |
| class Mutex { | |
| volatile bool InUse{false}; | |
| public: | |
| bool acquire() { return !__atomic_test_and_set(&InUse, __ATOMIC_ACQUIRE); } | |
| void release() { __atomic_clear(&InUse, __ATOMIC_RELEASE); } | |
| }; | |
| /// RAII wrapper for Mutex | |
| class Lock { | |
| Mutex &M; | |
| uint64_t SignalMask[1] = {}; | |
| public: | |
| Lock(Mutex &M) : M(M) { | |
| __sigprocmask(SIG_BLOCK, MaskAllSignals, SignalMask); | |
| while (!M.acquire()) { | |
| } | |
| } | |
| ~Lock() { | |
| M.release(); | |
| __sigprocmask(SIG_SETMASK, SignalMask, nullptr); | |
| } | |
| }; | |
| /// RAII wrapper for Mutex | |
| class TryLock { | |
| Mutex &M; | |
| bool Locked = false; | |
| public: | |
| TryLock(Mutex &M) : M(M) { | |
| int Retry = 100; | |
| while (--Retry && !M.acquire()) | |
| ; | |
| if (Retry) | |
| Locked = true; | |
| } | |
| bool isLocked() { return Locked; } | |
| ~TryLock() { | |
| if (isLocked()) | |
| M.release(); | |
| } | |
| }; | |
| inline uint64_t alignTo(uint64_t Value, uint64_t Align) { | |
| return (Value + Align - 1) / Align * Align; | |
| } | |
| } // anonymous namespace |