* Copyright (c) 2013-2023 Google, Inc. All rights reserved.
* Copyright (c) 2005-2010 VMware, Inc. All rights reserved.
* **********************************************************/
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#ifndef ASM_CODE_ONLY
# include "tools.h"
# ifdef UNIX
# include <unistd.h>
# include <sys/syscall.h>
# endif
# ifdef MACOS
# include <mach/mach.h>
# include <mach/semaphore.h>
# ifdef AARCH64
void
pthread_jit_write_protect_np(int enabled);
# endif
# endif
# define ASSERT_NOT_IMPLEMENTED() \
do { \
print("NYI\n"); \
abort(); \
} while (0)
# ifdef WINDOWS
int
get_windows_version(void)
{
OSVERSIONINFOW version;
typedef NTSTATUS(NTAPI * RtlGetVersion_t)(OSVERSIONINFOW * info);
RtlGetVersion_t RtlGetVersion;
NTSTATUS res;
HANDLE ntdll_handle = GetModuleHandleW(L"ntdll.dll");
assert(ntdll_handle != NULL);
RtlGetVersion =
(RtlGetVersion_t)GetProcAddress((HMODULE)ntdll_handle, "RtlGetVersion");
assert(RtlGetVersion != NULL);
version.dwOSVersionInfoSize = sizeof(version);
res = RtlGetVersion(&version);
assert(NT_SUCCESS(res));
if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
if (version.dwMajorVersion == 10 && version.dwMinorVersion == 0) {
if (GetProcAddress((HMODULE)ntdll_handle, "NtAllocateVirtualMemoryEx") !=
NULL)
return WINDOWS_VERSION_10_1803;
else if (GetProcAddress((HMODULE)ntdll_handle, "NtCallEnclave") != NULL)
return WINDOWS_VERSION_10_1709;
else if (GetProcAddress((HMODULE)ntdll_handle, "NtLoadHotPatch") != NULL)
return WINDOWS_VERSION_10_1703;
else if (GetProcAddress((HMODULE)ntdll_handle,
"NtCreateRegistryTransaction") != NULL)
return WINDOWS_VERSION_10_1607;
else if (GetProcAddress((HMODULE)ntdll_handle, "NtCreateEnclave") != NULL)
return WINDOWS_VERSION_10_1511;
else
return WINDOWS_VERSION_10;
} else if (version.dwMajorVersion == 6 && version.dwMinorVersion == 3) {
return WINDOWS_VERSION_8_1;
} else if (version.dwMajorVersion == 6 && version.dwMinorVersion == 2) {
return WINDOWS_VERSION_8;
} else if (version.dwMajorVersion == 6 && version.dwMinorVersion == 1) {
return WINDOWS_VERSION_7;
} else if (version.dwMajorVersion == 6 && version.dwMinorVersion == 0) {
return WINDOWS_VERSION_VISTA;
} else if (version.dwMajorVersion == 5 && version.dwMinorVersion == 2) {
return WINDOWS_VERSION_2003;
} else if (version.dwMajorVersion == 5 && version.dwMinorVersion == 1) {
return WINDOWS_VERSION_XP;
} else if (version.dwMajorVersion == 5 && version.dwMinorVersion == 0) {
return WINDOWS_VERSION_2000;
} else if (version.dwMajorVersion == 4) {
return WINDOWS_VERSION_NT;
}
}
return 0;
}
bool
is_wow64(HANDLE hProcess)
{
typedef DWORD(WINAPI * IsWow64Process_Type)(HANDLE hProcess, PBOOL isWow64Process);
static HANDLE kernel32_handle;
static IsWow64Process_Type IsWow64Process;
if (kernel32_handle == NULL)
kernel32_handle = GetModuleHandle("kernel32.dll");
if (IsWow64Process == NULL && kernel32_handle != NULL) {
IsWow64Process =
(IsWow64Process_Type)GetProcAddress(kernel32_handle, "IsWow64Process");
}
if (IsWow64Process == NULL) {
assert(get_windows_version() == WINDOWS_VERSION_NT ||
get_windows_version() == WINDOWS_VERSION_2000);
return false;
} else {
BOOL res;
if (!IsWow64Process(hProcess, &res))
return false;
return CAST_TO_bool(res);
}
}
# endif
int
get_os_prot_word(int prot)
{
# ifdef UNIX
return ((TEST(ALLOW_READ, prot) ? PROT_READ : 0) |
(TEST(ALLOW_WRITE, prot) ? PROT_WRITE : 0) |
(TEST(ALLOW_EXEC, prot) ? PROT_EXEC : 0));
# else
if (TEST(ALLOW_WRITE, prot)) {
if (TEST(ALLOW_EXEC, prot)) {
return PAGE_EXECUTE_READWRITE;
} else {
return PAGE_READWRITE;
}
} else {
if (TEST(ALLOW_READ, prot)) {
if (TEST(ALLOW_EXEC, prot)) {
return PAGE_EXECUTE_READ;
} else {
return PAGE_READONLY;
}
} else {
if (TEST(ALLOW_EXEC, prot)) {
return PAGE_EXECUTE;
} else {
return PAGE_NOACCESS;
}
}
}
# endif
}
char *
allocate_mem(size_t size, int prot)
{
# ifdef UNIX
int flags = MAP_PRIVATE | MAP_ANON;
# if defined(MACOS) && defined(AARCH64)
if (TEST(ALLOW_EXEC, prot)) {
flags |= MAP_JIT;
pthread_jit_write_protect_np(0);
}
# endif
char *res = (char *)mmap((void *)0, size, get_os_prot_word(prot), flags, -1, 0);
if (res == MAP_FAILED)
return NULL;
return res;
# else
return (char *)VirtualAlloc(NULL, size, MEM_COMMIT, get_os_prot_word(prot));
# endif
}
void
free_mem(char *addr, size_t size)
{
# ifdef UNIX
int res = munmap(addr, size);
if (res != 0)
print("Error on munmap: %d\n", errno);
# else
BOOL res = VirtualFree(addr, size, MEM_DECOMMIT);
if (!res)
print("Error on VirtualFree\n");
# endif
}
void
protect_mem(void *start, size_t len, int prot)
{
# ifdef UNIX
void *page_start = (void *)(((ptr_int_t)start) & ~(PAGE_SIZE - 1));
int page_len = (len + ((ptr_int_t)start - (ptr_int_t)page_start) + PAGE_SIZE - 1) &
~(PAGE_SIZE - 1);
# if defined(MACOS) && defined(AARCH64)
if (TEST(ALLOW_EXEC, prot) && !TEST(ALLOW_WRITE, prot)) {
pthread_jit_write_protect_np(1);
return;
}
# endif
if (mprotect(page_start, page_len, get_os_prot_word(prot)) != 0) {
print("Error on mprotect: %d\n", errno);
}
# else
DWORD old;
if (VirtualProtect(start, len, get_os_prot_word(prot), &old) == 0) {
print("Error on VirtualProtect\n");
}
# endif
}
void
protect_mem_check(void *start, size_t len, int prot, int expected)
{
# ifdef UNIX
protect_mem(start, len, prot);
# else
DWORD old;
if (VirtualProtect(start, len, get_os_prot_word(prot), &old) == 0) {
print("Error on VirtualProtect\n");
}
if (old != get_os_prot_word(expected)) {
print("Unexpected previous permissions\n");
}
# endif
}
void *
reserve_memory(int size)
{
# ifdef UNIX
void *p =
mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
if (p == MAP_FAILED)
return NULL;
return p;
# else
return VirtualAlloc(0, size, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
# endif
}
# define INTEL_EBX 0x756e6547
# define INTEL_EDX 0x49656e69
# define INTEL_ECX 0x6c65746e
# define AMD_EBX 0x68747541
# define AMD_EDX 0x69746e65
# define AMD_ECX 0x444d4163
# define VENDOR_INTEL 0
# define VENDOR_AMD 1
# define VENDOR_UNKNOWN 2
# define FAMILY_PENTIUM_IV 15
# define FAMILY_PENTIUM_III 6
# define FAMILY_PENTIUM_II 6
# define FAMILY_PENTIUM_PRO 6
# define FAMILY_ATHLON 6
# define FAMILY_PENTIUM 5
void
print(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fflush(stderr);
va_end(ap);
}
# ifdef UNIX
# define MAPS_LINE_LENGTH 4096
# define MAPS_LINE_FORMAT4 "%08lx-%08lx %s %*x %*s %*u %4096s"
# define MAPS_LINE_MAX4 49
# define MAPS_LINE_FORMAT8 "%016lx-%016lx %s %*x %*s %*u %4096s"
# define MAPS_LINE_MAX8 73
# define MAPS_LINE_MAX MAPS_LINE_MAX8
bool
find_dynamo_library(void)
{
pid_t pid = getpid();
char proc_pid_maps[64];
FILE *maps;
char line[MAPS_LINE_LENGTH];
int count = 0;
int n = snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid);
if (n < 0 || n == sizeof(proc_pid_maps))
assert(0);
maps = fopen(proc_pid_maps, "r");
while (!feof(maps)) {
void *vm_start;
void *vm_end;
char perm[16];
char comment_buffer[MAPS_LINE_LENGTH];
int len;
if (NULL == fgets(line, sizeof(line), maps))
break;
len = sscanf(line, sizeof(void *) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8,
(unsigned long *)&vm_start, (unsigned long *)&vm_end, perm,
comment_buffer);
if (len < 4)
comment_buffer[0] = '\0';
if (strstr(comment_buffer, "libdynamorio.so") != 0) {
fclose(maps);
return true;
}
}
fclose(maps);
return false;
}
* Staticly linked and stateless versions of libc routines.
*/
*/
int
nolibc_strlen(const char *str)
{
int i;
for (i = 0; str[i] != '\0'; i++) {
}
return i;
}
*/
void
nolibc_print(const char *str)
{
dynamorio_syscall(
# ifdef MACOS
SYS_write_nocancel,
# else
SYS_write,
# endif
3,
# if defined(MACOS) || defined(ANDROID)
stderr->_file,
# else
stderr->_fileno,
# endif
str, nolibc_strlen(str));
}
*/
void
nolibc_print_int(int n)
{
char buf[12];
int i = 0;
int m;
int len;
if (n < 0) {
buf[i++] = '-';
n = -n;
}
m = n;
while (m > 0) {
m /= 10;
i++;
}
len = i;
assert(len <= sizeof(buf));
while (n > 0) {
buf[--i] = '0' + (n % 10);
n /= 10;
}
buf[len] = '\0';
nolibc_print(buf);
}
*/
void
nolibc_nanosleep(struct timespec *req)
{
# ifdef MACOS
semaphore_t sem = MACH_PORT_NULL;
int res;
if (sem == MACH_PORT_NULL) {
kern_return_t res = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0);
assert(res == KERN_SUCCESS);
}
res = dynamorio_syscall(SYS___semwait_signal_nocancel, 6, sem, MACH_PORT_NULL, 1, 1,
(int64_t)req->tv_sec, (int32_t)req->tv_nsec);
# else
dynamorio_syscall(SYS_nanosleep, 2, req, NULL);
# endif
}
*/
void *
nolibc_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
{
# if defined(X64) || defined(MACOS)
int sysnum = SYS_mmap;
# else
int sysnum = SYS_mmap2;
# endif
return (void *)dynamorio_syscall(sysnum, 6, addr, length, prot, flags, fd, offset);
}
*/
int
nolibc_munmap(void *addr, size_t length)
{
return (int)dynamorio_syscall(SYS_munmap, 2, addr, length);
}
void
nolibc_memset(void *dst, int val, size_t size)
{
size_t i;
char *buf = (char *)dst;
for (i = 0; i < size; i++) {
buf[i] = (sbyte)val;
}
}
* Signal handling
*/
void
intercept_signal(int sig, handler_3_t handler, bool sigstack)
{
int rc;
struct sigaction act;
act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
rc = sigfillset(&act.sa_mask);
ASSERT_NOERR(rc);
act.sa_flags = SA_SIGINFO;
if (sigstack)
act.sa_flags |= SA_ONSTACK;
rc = sigaction(sig, &act, NULL);
ASSERT_NOERR(rc);
}
# endif
#else
* Assembly routines shared among multiple tests
*/
# include "asm_defines.asm"
START_FILE
* executes iters iters, by modifying the iters bound using self-modifying code.
*/
DECLARE_FUNC(code_self_mod)
GLOBAL_LABEL(code_self_mod:)
#ifdef X86
mov REG_XCX, ARG1
call next_instr
next_instr:
pop REG_XDX
* 3 == mov ecx into target
* 1 == opcode of target movl
*/
mov DWORD [REG_XDX + 5], ecx
mov eax,HEX(12345678)
mov ecx,0
repeat1:
dec eax
inc ecx
cmp eax,0
jnz repeat1
mov eax,ecx
ret
#elif defined(ARM)
adr r2, tomodify
strb ARG1, [r2]
asr ARG1, #8
mov r3, ARG1
bfc r3, #4, #4
strb r3, [r2, #1]
asr ARG1, #4
bfc ARG1, #4, #4
strb ARG1, [r2, #2]
mov r0, r2
add r1, r2, #4
mov r2, #0
push {r7}
movw r7, #0x0002
movt r7, #0x000f
svc #0
pop {r7}
tomodify:
movw r0, #0x1234
mov r1, #0
repeat1:
sub r0, r0, #1
add r1, r1, #1
cmp r0, #0
bne repeat1
mov r0, r1
bx lr
#elif defined(AARCH64)
adr x1, tomodify
ldr w2, [x1]
bfi w2, w0, #5, #16
str w2, [x1]
dc cvau, x1
dsb ish
ic ivau, x1
dsb ish
isb
tomodify:
movz w1, #0x1234
mov w0, #0
repeat1:
add w0, w0, #1
sub w1, w1, #1
cbnz w1, repeat1
ret
#elif defined(RISCV64)
ret
#else
# error NYI
#endif
END_FUNC(code_self_mod)
#undef FUNCNAME
#define FUNCNAME icache_sync
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#ifdef X86
ret
#elif defined(ARM)
mov r0, ARG1
add r1, r0, #64
mov r2, #0
push {r7}
movw r7, #0x0002
movt r7, #0x000f
svc #0
pop {r7}
bx lr
#elif defined(AARCH64)
mov x0, ARG1
dc cvau, x0
dsb ish
ic ivau, x0
dsb ish
isb
RETURN
#endif
END_FUNC(FUNCNAME)
#undef FUNCNAME
#define FUNCNAME code_inc
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#if !defined(RISCV64)
mov REG_SCRATCH0, ARG1
INC(REG_SCRATCH0)
RETURN
#endif
END_FUNC(FUNCNAME)
#undef FUNCNAME
#define FUNCNAME code_dec
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#if !defined(RISCV64)
mov REG_SCRATCH0, ARG1
DEC(REG_SCRATCH0)
RETURN
#endif
END_FUNC(FUNCNAME)
#undef FUNCNAME
#define FUNCNAME dummy
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#if !defined(RISCV64)
mov REG_SCRATCH0, HEX(1)
RETURN
#endif
END_FUNC(FUNCNAME)
#undef FUNCNAME
#define FUNCNAME call_with_retaddr
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#ifdef X86
lea REG_XAX, [REG_XSP]
xchg REG_XAX, ARG1
jmp REG_XAX
#elif defined(ARM)
push {r7, lr}
add r7, sp, #0
mov lr, r0
add r0, sp, #4
blx lr
pop {r7, pc}
#elif defined(AARCH64)
stp x29, x30, [sp, #-16]!
mov x29, sp
mov x30, x0
add x0, sp, #8
blr x30
ldp x29, x30, [sp], #16
ret
#elif defined(RISCV64)
ret
#else
# error NYI
#endif
END_FUNC(FUNCNAME)
#undef FUNCNAME
#define FUNCNAME tailcall_with_retaddr
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
#ifdef X86
mov REG_XAX, [REG_XSP]
xchg REG_XAX, ARG1
jmp REG_XAX
#elif defined(ARM)
mov r12, r0
mov r0, r14
bx r12
#elif defined(AARCH64)
mov x9, x0
mov x0, x30
br x9
#elif defined(RISCV64)
ret
#else
# error NYI
#endif
END_FUNC(FUNCNAME)
#ifdef AARCHXX
* roll our own.
*/
# undef FUNCNAME
# define FUNCNAME tools_clear_icache
DECLARE_FUNC(FUNCNAME)
GLOBAL_LABEL(FUNCNAME:)
# ifndef X64
push {r7}
mov r2, #0
movw r7, #0x0002
movt r7, #0x000f
svc #0
pop {r7}
bx lr
# else
b GLOBAL_REF(clear_icache)
# endif
END_FUNC(FUNCNAME)
#endif
END_FILE
#endif