| //===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// Platform specific utility functions. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "scudo_utils.h" | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdarg.h> | |
| #include <unistd.h> | |
| #include <cstring> | |
| // TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less | |
| // complicated string formatting code. The following is a | |
| // temporary workaround to be able to use __sanitizer::VSNPrintf. | |
| namespace __sanitizer { | |
| extern int VSNPrintf(char *buff, int buff_length, const char *format, | |
| va_list args); | |
| } // namespace __sanitizer | |
| namespace __scudo { | |
| FORMAT(1, 2) | |
| void dieWithMessage(const char *Format, ...) { | |
| // Our messages are tiny, 128 characters is more than enough. | |
| char Message[128]; | |
| va_list Args; | |
| va_start(Args, Format); | |
| __sanitizer::VSNPrintf(Message, sizeof(Message), Format, Args); | |
| va_end(Args); | |
| RawWrite(Message); | |
| Die(); | |
| } | |
| typedef struct { | |
| u32 Eax; | |
| u32 Ebx; | |
| u32 Ecx; | |
| u32 Edx; | |
| } CPUIDInfo; | |
| static void getCPUID(CPUIDInfo *info, u32 leaf, u32 subleaf) | |
| { | |
| asm volatile("cpuid" | |
| : "=a" (info->Eax), "=b" (info->Ebx), "=c" (info->Ecx), "=d" (info->Edx) | |
| : "a" (leaf), "c" (subleaf) | |
| ); | |
| } | |
| // Returns true is the CPU is a "GenuineIntel" or "AuthenticAMD" | |
| static bool isSupportedCPU() | |
| { | |
| CPUIDInfo Info; | |
| getCPUID(&Info, 0, 0); | |
| if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Genu", 4) == 0 && | |
| memcmp(reinterpret_cast<char *>(&Info.Edx), "ineI", 4) == 0 && | |
| memcmp(reinterpret_cast<char *>(&Info.Ecx), "ntel", 4) == 0) { | |
| return true; | |
| } | |
| if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Auth", 4) == 0 && | |
| memcmp(reinterpret_cast<char *>(&Info.Edx), "enti", 4) == 0 && | |
| memcmp(reinterpret_cast<char *>(&Info.Ecx), "cAMD", 4) == 0) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool testCPUFeature(CPUFeature feature) | |
| { | |
| static bool InfoInitialized = false; | |
| static CPUIDInfo CPUInfo = {}; | |
| if (InfoInitialized == false) { | |
| if (isSupportedCPU() == true) | |
| getCPUID(&CPUInfo, 1, 0); | |
| else | |
| UNIMPLEMENTED(); | |
| InfoInitialized = true; | |
| } | |
| switch (feature) { | |
| case SSE4_2: | |
| return ((CPUInfo.Ecx >> 20) & 0x1) != 0; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| // readRetry will attempt to read Count bytes from the Fd specified, and if | |
| // interrupted will retry to read additional bytes to reach Count. | |
| static ssize_t readRetry(int Fd, u8 *Buffer, size_t Count) { | |
| ssize_t AmountRead = 0; | |
| while (static_cast<size_t>(AmountRead) < Count) { | |
| ssize_t Result = read(Fd, Buffer + AmountRead, Count - AmountRead); | |
| if (Result > 0) | |
| AmountRead += Result; | |
| else if (!Result) | |
| break; | |
| else if (errno != EINTR) { | |
| AmountRead = -1; | |
| break; | |
| } | |
| } | |
| return AmountRead; | |
| } | |
| // Default constructor for Xorshift128Plus seeds the state with /dev/urandom | |
| Xorshift128Plus::Xorshift128Plus() { | |
| int Fd = open("/dev/urandom", O_RDONLY); | |
| bool Success = readRetry(Fd, reinterpret_cast<u8 *>(&State_0_), | |
| sizeof(State_0_)) == sizeof(State_0_); | |
| Success &= readRetry(Fd, reinterpret_cast<u8 *>(&State_1_), | |
| sizeof(State_1_)) == sizeof(State_1_); | |
| close(Fd); | |
| if (!Success) { | |
| dieWithMessage("ERROR: failed to read enough data from /dev/urandom.\n"); | |
| } | |
| } | |
| } // namespace __scudo |