| /* ********************************************************** |
| * Copyright (c) 2015-2022 Google, 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. |
| */ |
| |
| /* Default implementation to avoid the user of drlibc having to supply one. |
| * We declare this as weak for Linux and MacOS, and rely on MSVC prioritizing a |
| * .obj def over this .lib def (note that the MSVC linker only does that |
| * when the symbol in question is *by itself* in a single .obj: hence this file, |
| * rather than putting this into say asm_shared.asm). |
| */ |
| |
| /* Avoid pulling in deps from instr_inline.h included from globals.h */ |
| #define DR_NO_FAST_IR |
| |
| #include "../globals.h" /* just to disable warning C4206 about an empty file */ |
| |
| #ifdef MACOS |
| # include <sys/utsname.h> /* for struct utsname */ |
| #endif |
| |
| /*************************************************************************** |
| * Compatibility layer for sharing a single compiled-once library |
| * between core and non-core. |
| * Some are separated out into drlibc_notdr_* files since MSVC behaves |
| * better when duplicate symbols are each in their own .obj. |
| */ |
| |
| WEAK bool |
| safe_read_if_fast(const void *base, size_t size, void *out_buf) |
| { |
| return d_r_safe_read(base, size, out_buf); |
| } |
| |
| WEAK int |
| d_r_strcmp(const char *left, const char *right) |
| { |
| size_t i; |
| for (i = 0; left[i] != '\0' || right[i] != '\0'; i++) { |
| if (left[i] < right[i]) |
| return -1; |
| if (left[i] > right[i]) |
| return 1; |
| } |
| return 0; |
| } |
| |
| WEAK file_t main_logfile; |
| WEAK options_t dynamo_options; |
| |
| #ifdef MACOS |
| WEAK int |
| d_r_strncmp(const char *left, const char *right, size_t n) |
| { |
| size_t i; |
| for (i = 0; i < n && (left[i] != '\0' || right[i] != '\0'); i++) { |
| if (left[i] < right[i]) |
| return -1; |
| if (left[i] > right[i]) |
| return 1; |
| } |
| return 0; |
| } |
| |
| WEAK bool |
| kernel_is_64bit(void) |
| { |
| struct utsname uinfo; |
| if (uname(&uinfo) != 0) |
| return true; /* guess */ |
| return (strncmp(uinfo.machine, "x86_64", sizeof("x86_64")) == 0); |
| } |
| #endif |
| |
| /***************************************************************************/ |
| |
| #ifdef AARCH64 |
| |
| # ifdef MACOS |
| /* This is from libc. */ |
| extern void |
| sys_icache_invalidate(void *, size_t); |
| # endif |
| |
| void |
| clear_icache(void *beg, void *end) |
| { |
| # ifdef DR_HOST_NOT_TARGET |
| ASSERT_NOT_REACHED(); |
| # else |
| size_t dcache_line_size; |
| size_t icache_line_size; |
| ptr_uint_t beg_uint = (ptr_uint_t)beg; |
| ptr_uint_t end_uint = (ptr_uint_t)end; |
| ptr_uint_t addr; |
| |
| if (beg_uint >= end_uint) |
| return; |
| |
| if (!get_cache_line_size(&dcache_line_size, &icache_line_size)) { |
| /* We don't expect get_cache_line_size to return false, as this code is |
| * invoked only when (not DR_HOST_NOT_TARGET). |
| */ |
| ASSERT_NOT_REACHED(); |
| } |
| |
| /* Flush data cache to point of unification, one line at a time. */ |
| addr = ALIGN_BACKWARD(beg_uint, dcache_line_size); |
| do { |
| __asm__ __volatile__("dc cvau, %0" : : "r"(addr) : "memory"); |
| addr += dcache_line_size; |
| } while (addr != ALIGN_FORWARD(end_uint, dcache_line_size)); |
| |
| /* Data Synchronization Barrier */ |
| __asm__ __volatile__("dsb ish" : : : "memory"); |
| |
| /* Invalidate instruction cache to point of unification, one line at a time. */ |
| addr = ALIGN_BACKWARD(beg_uint, icache_line_size); |
| do { |
| __asm__ __volatile__("ic ivau, %0" : : "r"(addr) : "memory"); |
| addr += icache_line_size; |
| } while (addr != ALIGN_FORWARD(end_uint, icache_line_size)); |
| |
| /* Data Synchronization Barrier */ |
| __asm__ __volatile__("dsb ish" : : : "memory"); |
| |
| /* Instruction Synchronization Barrier */ |
| __asm__ __volatile__("isb" : : : "memory"); |
| |
| /* XXX i#5383: Do we need this in addition? This is from PR #5497. */ |
| IF_MACOS64(sys_icache_invalidate(beg, end_uint - beg_uint)); |
| |
| # endif |
| } |
| |
| /* Obtains dcache and icache line size and sets the values at the given pointers. |
| * Returns false if no value was written. |
| * This is required to be called at init time when linked into DR. This is to |
| * avoid races and write issues with the static variable used. |
| * |
| * XXX i#1684: Design better support for builds where host!=target, e.g. |
| * a64-on-x86 for which this function does not set any cache line size; also |
| * x86-on-a64 for which we currently attempt to use cpuid (which is not available |
| * on a64) to set cache line size in core/arch/x86/proc.c. |
| * For these builds, it may be better to set some properties like cache_line_size |
| * to the host's value, but not for all e.g. num_simd_registers. |
| */ |
| bool |
| get_cache_line_size(DR_PARAM_OUT size_t *dcache_line_size, |
| DR_PARAM_OUT size_t *icache_line_size) |
| { |
| # ifndef DR_HOST_NOT_TARGET |
| static size_t cache_info = 0; |
| |
| /* "Cache Type Register" contains: |
| * CTR_EL0 [31] : 1 |
| * CTR_EL0 [19:16] : Log2 of number of 4-byte words in smallest dcache line |
| * CTR_EL0 [3:0] : Log2 of number of 4-byte words in smallest icache line |
| * https://developer.arm.com/documentation/ddi0595/2021-09/AArch64-Registers/ |
| * CTR-EL0--Cache-Type-Register |
| * |
| * Also, the whitepaper below documents AArch64 words being 32 bits wide. |
| * https://developer.arm.com/-/media/Files/pdf/ |
| * graphics-and-multimedia/Porting%20to%20ARM%2064-bit.pdf |
| */ |
| if (cache_info == 0) { |
| # if defined(MACOS) |
| /* FIXME i#5383: Put in a proper solution; maybe getauxval() syscall with |
| * AT_HWCAP/AT_HWCAP2? |
| * mrs traps to illegal instruction on M1; |
| * hackily hardwire to "sysctl -a hw machdep.cpu" from one machine to |
| * make forward progress for now. |
| */ |
| cache_info = (1 << 31) | (7 << 16) | (7 << 0); |
| # else |
| __asm__ __volatile__("mrs %0, ctr_el0" : "=r"(cache_info)); |
| # endif |
| } |
| if (dcache_line_size != NULL) |
| *dcache_line_size = 4 << (cache_info >> 16 & 0xf); |
| if (icache_line_size != NULL) |
| *icache_line_size = 4 << (cache_info & 0xf); |
| return true; |
| # endif |
| return false; |
| } |
| #endif |
| |
| #ifdef UNIX |
| /* Parse the first line of a "#!" script. If the input is recognised, the string |
| * pointed to by str is overwritten with null terminators, as necessary, *interp |
| * is set to point at the script interpreter, and *arg to point at the optional |
| * argument, if there is one, or NULL. The accepted syntax is "#!", followed by |
| * optional spaces (' ' or '\t'), followed by the file path (any characters except |
| * spaces, '\n' and '\0'), optionally followed by the argument, followed by '\n' |
| * or '\0'. The argument may contain any character except '\n' and '\0', including |
| * spaces, but leading and trailing spaces are removed. |
| */ |
| static bool |
| is_shebang(DR_PARAM_OUT char *str, DR_PARAM_OUT char **interp, DR_PARAM_OUT char **arg) |
| { |
| char *p, *arg_end; |
| |
| if (str[0] != '#' || str[1] != '!') |
| return false; |
| p = str + 2; |
| while (*p == ' ' || *p == '\t') |
| ++p; |
| if (*p == '\n' || *p == '\0') |
| return false; |
| /* We have an interpreter. */ |
| *interp = p++; |
| while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') |
| ++p; |
| if (*p == '\n' || *p == '\0') { |
| *p = '\0'; |
| *arg = NULL; |
| return true; |
| } |
| *p++ = '\0'; |
| while (*p == ' ' || *p == '\t') |
| ++p; |
| if (*p == '\n' || *p == '\0') { |
| *arg = NULL; |
| return true; |
| } |
| /* We have an argument. */ |
| *arg = p++; |
| arg_end = p; |
| while (*p != '\n' && *p != '\0') { |
| if (*p != ' ' && *p != '\t') |
| arg_end = p + 1; |
| ++p; |
| } |
| *arg_end = '\0'; |
| return true; |
| } |
| |
| bool |
| find_script_interpreter(DR_PARAM_OUT script_interpreter_t *result, |
| DR_PARAM_IN const char *fname, |
| ssize_t (*reader)(const char *pathname, void *buf, size_t count)) |
| { |
| const int max_line_len = SCRIPT_LINE_MAX; |
| const int max_recursion = SCRIPT_RECURSION_MAX; |
| char **argv = result->argv; |
| char *interp, *arg; |
| const char *file; |
| ssize_t len; |
| int i, argc; |
| |
| file = fname; |
| for (i = 0; i < max_recursion; i++) { |
| len = reader(file, result->buffer[i], max_line_len); |
| if (len < 0) |
| break; |
| result->buffer[0][len] = 0; |
| if (!is_shebang(result->buffer[i], &interp, &arg)) |
| break; |
| |
| /* Add strings to argv: arg first as we will reverse later. */ |
| if (arg != NULL) |
| *argv++ = arg; |
| *argv++ = interp; |
| |
| file = interp; |
| } |
| |
| if (i == 0) |
| return false; |
| |
| if (i == max_recursion) { |
| /* Check that the final script interpreter is not itself a script. */ |
| char tmp[sizeof(result->buffer)]; |
| len = reader(*(argv - 1), tmp, max_line_len); |
| if (len >= 0) { |
| tmp[len] = 0; |
| if (is_shebang(tmp, &interp, &arg)) { |
| result->argc = 0; |
| result->argv[0] = NULL; |
| return true; |
| } |
| } |
| } |
| |
| argc = argv - result->argv; |
| result->argc = argc; |
| /* Reverse order of arguments and null-terminate. */ |
| for (i = 0; i < argc / 2; i++) { |
| char *tmp = result->argv[i]; |
| result->argv[i] = result->argv[argc - 1 - i]; |
| result->argv[argc - 1 - i] = tmp; |
| } |
| result->argv[argc] = NULL; |
| return true; |
| } |
| #endif |
| |
| #ifdef WINDOWS |
| /*************************************************************************** |
| * Windows mode switching support. |
| */ |
| |
| /* We set a default equal to the observed 0x2b value on every Windows version. |
| * The core calls d_r_set_ss_selector() to update to the underlying value. |
| */ |
| int d_r_ss_value = 0x2b; |
| #endif |