|
From: Paul F. <pa...@so...> - 2021-10-09 12:13:50
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=e2e5d75f5cbc108d68142c4c56878df9abaed653 commit e2e5d75f5cbc108d68142c4c56878df9abaed653 Author: Paul Floyd <pj...@wa...> Date: Sat Oct 9 14:11:48 2021 +0200 FreeBSD support, patch 11 coregrind added files the key mechanics and the syscall wrappers Diff: --- configure.ac | 2 + coregrind/Makefile.am | 11 + coregrind/launcher-freebsd.c | 325 ++ coregrind/link_tool_exe_freebsd.in | 82 + coregrind/m_dispatch/dispatch-amd64-freebsd.S | 321 ++ coregrind/m_dispatch/dispatch-x86-freebsd.S | 251 + coregrind/m_initimg/initimg-freebsd.c | 979 ++++ coregrind/m_sigframe/sigframe-amd64-freebsd.c | 432 ++ coregrind/m_sigframe/sigframe-x86-freebsd.c | 457 ++ coregrind/m_syswrap/priv_syswrap-freebsd.h | 554 ++ coregrind/m_syswrap/syscall-amd64-freebsd.S | 207 + coregrind/m_syswrap/syscall-x86-freebsd.S | 201 + coregrind/m_syswrap/syswrap-amd64-freebsd.c | 1113 ++++ coregrind/m_syswrap/syswrap-freebsd.c | 6876 +++++++++++++++++++++++++ coregrind/m_syswrap/syswrap-x86-freebsd.c | 1517 ++++++ 15 files changed, 13328 insertions(+) diff --git a/configure.ac b/configure.ac index 39e619575e..275c0ca02c 100755 --- a/configure.ac +++ b/configure.ac @@ -5211,6 +5211,8 @@ AC_CONFIG_FILES([ ]) AC_CONFIG_FILES([coregrind/link_tool_exe_linux], [chmod +x coregrind/link_tool_exe_linux]) +AC_CONFIG_FILES([coregrind/link_tool_exe_freebsd], + [chmod +x coregrind/link_tool_exe_freebsd]) AC_CONFIG_FILES([coregrind/link_tool_exe_darwin], [chmod +x coregrind/link_tool_exe_darwin]) AC_CONFIG_FILES([coregrind/link_tool_exe_solaris], diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 01b8783ee4..1de685bb45 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -286,6 +286,7 @@ noinst_HEADERS = \ m_syswrap/priv_syswrap-generic.h \ m_syswrap/priv_syswrap-linux.h \ m_syswrap/priv_syswrap-linux-variants.h \ + m_syswrap/priv_syswrap-freebsd.h \ m_syswrap/priv_syswrap-darwin.h \ m_syswrap/priv_syswrap-solaris.h \ m_syswrap/priv_syswrap-main.h \ @@ -388,6 +389,8 @@ COREGRIND_SOURCES_COMMON = \ m_dispatch/dispatch-mips32-linux.S \ m_dispatch/dispatch-mips64-linux.S \ m_dispatch/dispatch-nanomips-linux.S \ + m_dispatch/dispatch-x86-freebsd.S \ + m_dispatch/dispatch-amd64-freebsd.S \ m_dispatch/dispatch-x86-darwin.S \ m_dispatch/dispatch-amd64-darwin.S \ m_dispatch/dispatch-x86-solaris.S \ @@ -412,6 +415,7 @@ COREGRIND_SOURCES_COMMON = \ m_gdbserver/valgrind-low-nanomips.c \ m_gdbserver/version.c \ m_initimg/initimg-linux.c \ + m_initimg/initimg-freebsd.c \ m_initimg/initimg-darwin.c \ m_initimg/initimg-solaris.c \ m_mach/mach_basics.c \ @@ -426,6 +430,8 @@ COREGRIND_SOURCES_COMMON = \ m_sigframe/sigframe-common.c \ m_sigframe/sigframe-x86-linux.c \ m_sigframe/sigframe-amd64-linux.c \ + m_sigframe/sigframe-x86-freebsd.c \ + m_sigframe/sigframe-amd64-freebsd.c \ m_sigframe/sigframe-ppc32-linux.c \ m_sigframe/sigframe-ppc64-linux.c \ m_sigframe/sigframe-arm-linux.c \ @@ -448,6 +454,8 @@ COREGRIND_SOURCES_COMMON = \ m_syswrap/syscall-mips32-linux.S \ m_syswrap/syscall-mips64-linux.S \ m_syswrap/syscall-nanomips-linux.S \ + m_syswrap/syscall-x86-freebsd.S \ + m_syswrap/syscall-amd64-freebsd.S \ m_syswrap/syscall-x86-darwin.S \ m_syswrap/syscall-amd64-darwin.S \ m_syswrap/syscall-x86-solaris.S \ @@ -456,12 +464,15 @@ COREGRIND_SOURCES_COMMON = \ m_syswrap/syswrap-generic.c \ m_syswrap/syswrap-linux.c \ m_syswrap/syswrap-linux-variants.c \ + m_syswrap/syswrap-freebsd.c \ m_syswrap/syswrap-darwin.c \ m_syswrap/syswrap-solaris.c \ m_syswrap/syswrap-x86-linux.c \ m_syswrap/syswrap-amd64-linux.c \ m_syswrap/syswrap-ppc32-linux.c \ m_syswrap/syswrap-ppc64-linux.c \ + m_syswrap/syswrap-x86-freebsd.c \ + m_syswrap/syswrap-amd64-freebsd.c \ m_syswrap/syswrap-arm-linux.c \ m_syswrap/syswrap-arm64-linux.c \ m_syswrap/syswrap-s390x-linux.c \ diff --git a/coregrind/launcher-freebsd.c b/coregrind/launcher-freebsd.c new file mode 100644 index 0000000000..7d40ad6cce --- /dev/null +++ b/coregrind/launcher-freebsd.c @@ -0,0 +1,325 @@ + +/*--------------------------------------------------------------------*/ +/*--- Launching valgrind m_launcher.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + js...@ac... + Copyright (C) 2018-2021 Paul Floyd + pj...@wa... + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Note: this is a "normal" program and not part of Valgrind proper, + and so it doesn't have to conform to Valgrind's arcane rules on + no-glibc-usage etc. */ + +#include <assert.h> +#include <ctype.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +/* #include <sys/user.h> */ +#include <unistd.h> + +#include "pub_core_debuglog.h" +#include "pub_core_vki.h" // Avoids warnings from +// pub_core_libcfile.h +#include "pub_core_libcproc.h" // For VALGRIND_LIB, VALGRIND_LAUNCHER +#include "pub_core_ume.h" + + +#if !defined(PATH_MAX) +#define PATH_MAX 4096 /* POSIX refers to this a lot but I dunno + where it is defined */ +#endif + +#ifndef EM_X86_64 +#define EM_X86_64 62 // elf.h doesn't define this on some older systems +#endif + +/* Report fatal errors */ +__attribute__((noreturn)) +static void barf ( const char *format, ... ) +{ + va_list vargs; + + va_start(vargs, format); + fprintf(stderr, "valgrind: Cannot continue: "); + vfprintf(stderr, format, vargs); + fprintf(stderr, "\n"); + va_end(vargs); + + exit(1); + /*NOTREACHED*/ + assert(0); +} + +/* Search the path for the client program */ +static const char *find_client(const char *clientname) +{ + static char fullname[PATH_MAX]; + const char *path = getenv("PATH"); + const char *colon; + + while (path) { + if ((colon = strchr(path, ':')) == NULL) { + strcpy(fullname, path); + path = NULL; + } else { + memcpy(fullname, path, colon - path); + fullname[colon - path] = '\0'; + path = colon + 1; + } + + strcat(fullname, "/"); + strcat(fullname, clientname); + + if (access(fullname, R_OK|X_OK) == 0) + return fullname; + } + + return clientname; +} + +/* Examine the client and work out which platform it is for */ +static const char *select_platform(const char *clientname) +{ + int fd; + char header[4096]; + ssize_t n_bytes; + const char *platform = NULL; + + VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); + + if (strchr(clientname, '/') == NULL) + clientname = find_client(clientname); + + if ((fd = open(clientname, O_RDONLY)) < 0) + return NULL; + // barf("open(%s): %s", clientname, strerror(errno)); + + n_bytes = read(fd, header, sizeof(header)); + close(fd); + if (n_bytes < 2) { + return NULL; + } + + if (header[0] == '#' && header[1] == '!') { + int i = 2; + char *interp = (char *)header + 2; + + // Skip whitespace. + while (1) { + if (i == n_bytes) return NULL; + if (' ' != header[i] && '\t' == header[i]) break; + i++; + } + + // Get the interpreter name. + interp = &header[i]; + while (1) { + if (i == n_bytes) break; + if (isspace(header[i])) break; + i++; + } + if (i == n_bytes) return NULL; + header[i] = '\0'; + + platform = select_platform(interp); + + } else if (n_bytes >= SELFMAG && memcmp(header, ELFMAG, SELFMAG) == 0) { + + if ((size_t)n_bytes >= sizeof(Elf32_Ehdr) && header[EI_CLASS] == ELFCLASS32) { + const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header; + + if (header[EI_DATA] == ELFDATA2LSB) { + if (ehdr->e_machine == EM_386 && + ehdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { + platform = "x86-freebsd"; + } + } + } else if ((size_t)n_bytes >= sizeof(Elf64_Ehdr) && header[EI_CLASS] == ELFCLASS64) { + const Elf64_Ehdr *ehdr = (Elf64_Ehdr *)header; + + if (header[EI_DATA] == ELFDATA2LSB) { + if (ehdr->e_machine == EM_X86_64 && + ehdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { + platform = "amd64-freebsd"; + } + } + } + } + + VG_(debugLog)(2, "launcher", "selected platform '%s'\n", + platform ? platform : "unknown"); + + return platform; +} + +/* Where we expect to find all our aux files */ +static const char *valgrind_lib = VG_LIBDIR; + +int main(int argc, char** argv, char** envp) +{ + int i, j, loglevel, r; + const char *toolname = NULL; + const char *clientname = NULL; + const char *platform; + const char *default_platform; + const char *cp; + char *toolfile; + char launcher_name[PATH_MAX+1]; + char* new_line; + char** new_env; + int oid[4]; + vki_size_t len; + + /* Start the debugging-log system ASAP. First find out how many + "-d"s were specified. This is a pre-scan of the command line. + At the same time, look for the tool name. */ + loglevel = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + clientname = argv[i]; + break; + } + if (0 == strcmp(argv[i], "--")) { + if (i+1 < argc) + clientname = argv[i+1]; + break; + } + if (0 == strcmp(argv[i], "-d")) + loglevel++; + if (0 == strncmp(argv[i], "--tool=", 7)) + toolname = argv[i] + 7; + } + + /* ... and start the debug logger. Now we can safely emit logging + messages all through startup. */ + VG_(debugLog_startup)(loglevel, "Stage 1"); + + /* Make sure we know which tool we're using */ + if (toolname) { + VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname); + } else { + VG_(debugLog)(1, "launcher", + "no tool requested, defaulting to 'memcheck'\n"); + toolname = "memcheck"; + } + + /* Select a platform to use if we can't decide that by looking at + the executable (eg because it's a shell script). Note that the + default_platform is not necessarily either the primary or + secondary build target. Instead it's chosen to maximise the + chances that /bin/sh will work on it. Hence for a primary + target of ppc64-linux we still choose ppc32-linux as the default + target, because on most ppc64-linux setups, the basic /bin, + /usr/bin, etc, stuff is built in 32-bit mode, not 64-bit + mode. */ + if (0==strcmp(VG_PLATFORM,"x86-freebsd")) + default_platform = "x86-freebsd"; + else if (0==strcmp(VG_PLATFORM,"amd64-freebsd")) + default_platform = "amd64-freebsd"; + else + barf("Unknown VG_PLATFORM '%s'", VG_PLATFORM); + + /* Work out what platform to use, or use the default platform if + not possible. */ + if (clientname == NULL) { + VG_(debugLog)(1, "launcher", + "no client specified, defaulting platform to '%s'\n", + default_platform); + platform = default_platform; + } else if ((platform = select_platform(clientname)) != NULL) { + VG_(debugLog)(1, "launcher", "selected platform '%s'\n", platform); + } else { + VG_(debugLog)(1, "launcher", + "no platform detected, defaulting platform to '%s'\n", + default_platform); + platform = default_platform; + } + + /* Figure out the name of this executable (viz, the launcher), so + we can tell stage2. stage2 will use the name for recursive + invocations of valgrind on child processes. */ + memset(launcher_name, 0, PATH_MAX+1); + + oid[0] = CTL_KERN; + oid[1] = KERN_PROC; + oid[2] = KERN_PROC_PATHNAME; + oid[3] = getpid(); + len = PATH_MAX; + r = sysctl(oid, 4, launcher_name, &len, 0, 0); + if (r != 0) { + fprintf(stderr, "valgrind: warning (non-fatal): " + "sysctl(\"kern.proc.pathname\") failed.\n"); + fprintf(stderr, "valgrind: continuing, however --trace-children=yes " + "will not work.\n"); + } + + /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */ + new_line = malloc(strlen(VALGRIND_LAUNCHER) + 1 + + strlen(launcher_name) + 1); + if (new_line == NULL) + barf("malloc of new_line failed."); + strcpy(new_line, VALGRIND_LAUNCHER); + strcat(new_line, "="); + strcat(new_line, launcher_name); + + for (j = 0; envp[j]; j++) + ; + new_env = malloc((j+2) * sizeof(char*)); + if (new_env == NULL) + barf("malloc of new_env failed."); + for (i = 0; i < j; i++) + new_env[i] = envp[i]; + new_env[i++] = new_line; + new_env[i++] = NULL; + assert(i == j+2); + + /* Establish the correct VALGRIND_LIB. */ + cp = getenv(VALGRIND_LIB); + + if (cp != NULL) + valgrind_lib = cp; + + /* Build the stage2 invocation, and execve it. Bye! */ + toolfile = malloc(strlen(valgrind_lib) + strlen(toolname) + strlen(platform) + 3); + if (toolfile == NULL) + barf("malloc of toolfile failed."); + sprintf(toolfile, "%s/%s-%s", valgrind_lib, toolname, platform); + + VG_(debugLog)(1, "launcher", "launching %s\n", toolfile); + + execve(toolfile, argv, new_env); + + fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s': %s\n", + toolname, platform, strerror(errno)); + + exit(1); +} diff --git a/coregrind/link_tool_exe_freebsd.in b/coregrind/link_tool_exe_freebsd.in new file mode 100755 index 0000000000..b4df4622c1 --- /dev/null +++ b/coregrind/link_tool_exe_freebsd.in @@ -0,0 +1,82 @@ +#! @PERL@ + +# This script handles linking the tool executables on FreeBSD, +# statically and at an alternative load address. +# +# Linking statically sidesteps all sorts of complications to do with +# having two copies of the dynamic linker (valgrind's and the +# client's) coexisting in the same process. The alternative load +# address is needed because Valgrind itself will load the client at +# whatever address it specifies, which is almost invariably the +# default load address. Hence we can't allow Valgrind itself (viz, +# the tool executable) to be loaded at that address. +# +# Unfortunately there's no standard way to do 'static link at +# alternative address', so these link_tool_exe_*.in scripts handle +# the per-platform hoop-jumping. +# +# What we get passed here is: +# first arg +# the alternative load address +# all the rest of the args +# the compiler invocation to do the final link, that +# the build system would have done, left to itself +# +# We just let the script 'die' if something is wrong, rather than do +# proper error reporting. We don't expect the users to run this +# directly. It is only run as part of the build process, with +# carefully constrained inputs. +# +# FreeBSD specific complications: +# +# - in the initial version of this file, the linker(s) it was targeted +# at supported only -Ttext to load the code at an alternative address, +# and did not require removing the build notes in order to function +# correctly, so the work done by configure to determine what should go +# into the FLAG_T_TEXT was ignored. +# +# - LLVM's ld.lld, for at least versions 8.0 (shipping with FreeBSD 12.1) +# and 9.0 support the -Ttext option and behave as desired. As of +# LLVM ld.lld version 10.0 a breaking change made -Ttext unusable, +# however the --image-base option has the desired semantics. +# It turns out that ld.lld has supported --image-base since at least +# as far back as version 8.0. +# +# So: what we actually do: +# +# pass the specified command to the linker as-is, except, add +# "-static" and the value of FLAG_T_TEXT as determined by configure. +# Previously we did this by adding these options after the first +# word of the rest of the arguments, which works in the common case +# when it's something like "gcc". But the linker invocation itself +# might be multiple words, say if it's "ccache gcc". So we now put +# the new options at the end instead. +# + +use warnings; +use strict; + +# expect at least: alt-load-address gcc -o foo bar.o +die "Not enough arguments" + if (($#ARGV + 1) < 5); + +my $ala = $ARGV[0]; +shift; # Remove $ala from @ARGV + +# check for plausible-ish alt load address +die "Bogus alt-load address" + if (length($ala) < 3 || index($ala, "0x") != 0); + +my $cmd = join(" ", @ARGV, "-static -Wl,@FLAG_T_TEXT@=$ala"); + +#print "link_tool_exe_freebsd: $cmd\n"; + + +# Execute the command: +my $r = system("$cmd"); + +if ($r == 0) { + exit 0; +} else { + exit 1; +} diff --git a/coregrind/m_dispatch/dispatch-amd64-freebsd.S b/coregrind/m_dispatch/dispatch-amd64-freebsd.S new file mode 100644 index 0000000000..db4dccac76 --- /dev/null +++ b/coregrind/m_dispatch/dispatch-amd64-freebsd.S @@ -0,0 +1,321 @@ + +/*--------------------------------------------------------------------*/ +/*--- The core dispatch loop, for jumping to a code address. ---*/ +/*--- dispatch-amd64-freebsd.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2017 Julian Seward + js...@ac... + Copyright (C) 2018-2021 Paul Floyd + pj...@wa... + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" + +#if defined(VGP_amd64_freebsd) + +#include "pub_core_dispatch_asm.h" +#include "pub_core_transtab_asm.h" +#include "libvex_guest_offsets.h" /* for OFFSET_amd64_RIP */ + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- The dispatch loop. VG_(disp_run_translations) is ---*/ +/*--- used to run all translations, ---*/ +/*--- including no-redir ones. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/*----------------------------------------------------*/ +/*--- Entry and preamble (set everything up) ---*/ +/*----------------------------------------------------*/ + +/* signature: +void VG_(disp_run_translations)( UWord* two_words, + void* guest_state, + Addr host_addr ); +*/ +.text +.globl VG_(disp_run_translations) +.type VG_(disp_run_translations), @function +VG_(disp_run_translations): + /* %rdi holds two_words */ + /* %rsi holds guest_state */ + /* %rdx holds host_addr */ + + /* The preamble */ + + /* Save integer registers, since this is a pseudo-function. */ + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rbp + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + /* %rdi must be saved last */ + pushq %rdi + + /* Get the host CPU in the state expected by generated code. */ + + /* set host FPU control word to the default mode expected + by VEX-generated code. See comments in libvex.h for + more info. */ + finit + pushq $0x027F + fldcw (%rsp) + addq $8, %rsp + + /* set host SSE control word to the default mode expected + by VEX-generated code. */ + pushq $0x1F80 + ldmxcsr (%rsp) + addq $8, %rsp + + /* set dir flag to known value */ + cld + + /* Set up the guest state pointer */ + movq %rsi, %rbp + + /* and jump into the code cache. Chained translations in + the code cache run, until for whatever reason, they can't + continue. When that happens, the translation in question + will jump (or call) to one of the continuation points + VG_(cp_...) below. */ + jmpq *%rdx + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- Postamble and exit. ---*/ +/*----------------------------------------------------*/ + +postamble: + /* At this point, %rax and %rdx contain two + words to be returned to the caller. %rax + holds a TRC value, and %rdx optionally may + hold another word (for CHAIN_ME exits, the + address of the place to patch.) */ + + /* We're leaving. Check that nobody messed with %mxcsr + or %fpucw. We can't mess with %rax or %rdx here as they + hold the tentative return values, but any others are OK. */ +#if !defined(ENABLE_INNER) + /* This check fails for self-hosting, so skip in that case */ + pushq $0 + fstcw (%rsp) + cmpl $0x027F, (%rsp) + popq %r15 /* get rid of the word without trashing %rflags */ + jnz invariant_violation +#endif + pushq $0 + stmxcsr (%rsp) + andl $0xFFFFFFC0, (%rsp) /* mask out status flags */ + cmpl $0x1F80, (%rsp) + popq %r15 + jnz invariant_violation + /* otherwise we're OK */ + jmp remove_frame +invariant_violation: + movq $VG_TRC_INVARIANT_FAILED, %rax + movq $0, %rdx + +remove_frame: + /* Pop %rdi, stash return values */ + popq %rdi + movq %rax, 0(%rdi) + movq %rdx, 8(%rdi) + /* Now pop everything else */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rbp + popq %rsi + popq %rdx + popq %rcx + popq %rbx + popq %rax + ret + +/*----------------------------------------------------*/ +/*--- Continuation points ---*/ +/*----------------------------------------------------*/ + +/* ------ Chain me to slow entry point ------ */ +.global VG_(disp_cp_chain_me_to_slowEP) +VG_(disp_cp_chain_me_to_slowEP): + /* We got called. The return address indicates + where the patching needs to happen. Collect + the return address and, exit back to C land, + handing the caller the pair (Chain_me_S, RA) */ + movq $VG_TRC_CHAIN_ME_TO_SLOW_EP, %rax + popq %rdx + /* 10 = movabsq $VG_(disp_chain_me_to_slowEP), %r11; + 3 = call *%r11 */ + subq $10+3, %rdx + jmp postamble + +/* ------ Chain me to fast entry point ------ */ +.global VG_(disp_cp_chain_me_to_fastEP) +VG_(disp_cp_chain_me_to_fastEP): + /* We got called. The return address indicates + where the patching needs to happen. Collect + the return address and, exit back to C land, + handing the caller the pair (Chain_me_F, RA) */ + movq $VG_TRC_CHAIN_ME_TO_FAST_EP, %rax + popq %rdx + /* 10 = movabsq $VG_(disp_chain_me_to_fastEP), %r11; + 3 = call *%r11 */ + subq $10+3, %rdx + jmp postamble + +/* ------ Indirect but boring jump ------ */ +.global VG_(disp_cp_xindir) +VG_(disp_cp_xindir): + /* Where are we going? */ + movq OFFSET_amd64_RIP(%rbp), %rax // "guest" + + /* stats only */ + addl $1, VG_(stats__n_xIndirs_32) + + // LIVE: %rbp (guest state ptr), %rax (guest address to go to). + // We use 4 temporaries: + // %r9 (to point at the relevant FastCacheSet), + // %r10, %r11 and %r12 (scratch). + + /* Try a fast lookup in the translation cache. This is pretty much + a handcoded version of VG_(lookupInFastCache). */ + + // Compute %r9 = VG_TT_FAST_HASH(guest) + movq %rax, %r9 // guest + shrq $VG_TT_FAST_BITS, %r9 // (guest >> VG_TT_FAST_BITS) + xorq %rax, %r9 // (guest >> VG_TT_FAST_BITS) ^ guest + andq $VG_TT_FAST_MASK, %r9 // setNo + + // Compute %r9 = &VG_(tt_fast)[%r9] + shlq $VG_FAST_CACHE_SET_BITS, %r9 // setNo * sizeof(FastCacheSet) + movabsq $VG_(tt_fast), %r10 // &VG_(tt_fast)[0] + leaq (%r10, %r9), %r9 // &VG_(tt_fast)[setNo] + + // LIVE: %rbp (guest state ptr), %rax (guest addr), %r9 (cache set) + // try way 0 + cmpq %rax, FCS_g0(%r9) // cmp against .guest0 + jnz 1f + // hit at way 0 + jmp *FCS_h0(%r9) // goto .host0 + ud2 + +1: // try way 1 + cmpq %rax, FCS_g1(%r9) // cmp against .guest1 + jnz 2f + // hit at way 1; swap upwards + /* stats only */ + addl $1, VG_(stats__n_xIndir_hits1_32) + movq FCS_g0(%r9), %r10 // r10 = old .guest0 + movq FCS_h0(%r9), %r11 // r11 = old .host0 + movq FCS_h1(%r9), %r12 // r12 = old .host1 + movq %rax, FCS_g0(%r9) // new .guest0 = guest + movq %r12, FCS_h0(%r9) // new .host0 = old .host1 + movq %r10, FCS_g1(%r9) // new .guest1 = old .guest0 + movq %r11, FCS_h1(%r9) // new .host1 = old .host0 + jmp *%r12 // goto old .host1 a.k.a. new .host0 + ud2 + +2: // try way 2 + cmpq %rax, FCS_g2(%r9) // cmp against .guest2 + jnz 3f + // hit at way 2; swap upwards + /* stats only */ + addl $1, VG_(stats__n_xIndir_hits2_32) + movq FCS_g1(%r9), %r10 + movq FCS_h1(%r9), %r11 + movq FCS_h2(%r9), %r12 + movq %rax, FCS_g1(%r9) + movq %r12, FCS_h1(%r9) + movq %r10, FCS_g2(%r9) + movq %r11, FCS_h2(%r9) + jmp *%r12 + ud2 + +3: // try way 3 + cmpq %rax, FCS_g3(%r9) // cmp against .guest3 + jnz 4f + // hit at way 3; swap upwards + /* stats only */ + addl $1, VG_(stats__n_xIndir_hits3_32) + movq FCS_g2(%r9), %r10 + movq FCS_h2(%r9), %r11 + movq FCS_h3(%r9), %r12 + movq %rax, FCS_g2(%r9) + movq %r12, FCS_h2(%r9) + movq %r10, FCS_g3(%r9) + movq %r11, FCS_h3(%r9) + jmp *%r12 + ud2 + +4: // fast lookup failed + /* stats only */ + addl $1, VG_(stats__n_xIndir_misses_32) + + movq $VG_TRC_INNER_FASTMISS, %rax + movq $0, %rdx + jmp postamble + +/* ------ Assisted jump ------ */ +.global VG_(disp_cp_xassisted) +VG_(disp_cp_xassisted): + /* %rbp contains the TRC */ + movq %rbp, %rax + movq $0, %rdx + jmp postamble + +/* ------ Event check failed ------ */ +.global VG_(disp_cp_evcheck_fail) +VG_(disp_cp_evcheck_fail): + movq $VG_TRC_INNER_COUNTERZERO, %rax + movq $0, %rdx + jmp postamble + + +.size VG_(disp_run_translations), .-VG_(disp_run_translations) + +#endif // defined(VGP_amd64_freebsd) + +/* Let the linker know we don't need an executable stack */ +MARK_STACK_NO_EXEC + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_dispatch/dispatch-x86-freebsd.S b/coregrind/m_dispatch/dispatch-x86-freebsd.S new file mode 100644 index 0000000000..667bfcd96a --- /dev/null +++ b/coregrind/m_dispatch/dispatch-x86-freebsd.S @@ -0,0 +1,251 @@ + +/*--------------------------------------------------------------------*/ +/*--- The core dispatch loop, for jumping to a code address. ---*/ +/*--- dispatch-x86-freebsd.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2012 Julian Seward + js...@ac... + Copyright (C) 2018-2021 Paul Floyd + pj...@wa... + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" + +#if defined(VGP_x86_freebsd) + +#include "pub_core_dispatch_asm.h" +#include "pub_core_transtab_asm.h" +#include "libvex_guest_offsets.h" /* for OFFSET_x86_EIP */ + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- The dispatch loop. VG_(disp_run_translations) is ---*/ +/*--- used to run all translations, ---*/ +/*--- including no-redir ones. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/*----------------------------------------------------*/ +/*--- Entry and preamble (set everything up) ---*/ +/*----------------------------------------------------*/ + +/* signature: +void VG_(disp_run_translations)( UWord* two_words, + void* guest_state, + Addr host_addr ); +*/ +.text +.globl VG_(disp_run_translations) +.type VG_(disp_run_translations), @function +VG_(disp_run_translations): + /* 0(%esp) holds our return address. */ + /* 4(%esp) holds two_words */ + /* 8(%esp) holds guest_state */ + /* 12(%esp) holds host_addr */ + + /* The preamble */ + + /* Save integer registers, since this is a pseudo-function. */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushl %ebp + + /* 28+4(%esp) holds two_words */ + /* 28+8(%esp) holds guest_state */ + /* 28+12(%esp) holds host_addr */ + + /* Get the host CPU in the state expected by generated code. */ + + /* set host FPU control word to the default mode expected + by VEX-generated code. See comments in libvex.h for + more info. */ + finit + pushl $0x027F + fldcw (%esp) + addl $4, %esp + + /* set host SSE control word to the default mode expected + by VEX-generated code. */ + cmpl $0, VG_(machine_x86_have_mxcsr) + jz L1 + pushl $0x1F80 + ldmxcsr (%esp) + addl $4, %esp +L1: + /* set dir flag to known value */ + cld + + /* Set up the guest state pointer */ + movl 28+8(%esp), %ebp + + /* and jump into the code cache. Chained translations in + the code cache run, until for whatever reason, they can't + continue. When that happens, the translation in question + will jump (or call) to one of the continuation points + VG_(cp_...) below. */ + jmpl *28+12(%esp) + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- Postamble and exit. ---*/ +/*----------------------------------------------------*/ + +postamble: + /* At this point, %eax and %edx contain two + words to be returned to the caller. %eax + holds a TRC value, and %edx optionally may + hold another word (for CHAIN_ME exits, the + address of the place to patch.) */ + + /* We're leaving. Check that nobody messed with %mxcsr + or %fpucw. We can't mess with %eax or %edx here as they + holds the tentative return value, but any others are OK. */ +#if !defined(ENABLE_INNER) + /* This check fails for self-hosting, so skip in that case */ + pushl $0 + fstcw (%esp) + cmpl $0x027F, (%esp) + popl %esi /* get rid of the word without trashing %eflags */ + jnz invariant_violation +#endif +# cmpl $0, VG_(machine_x86_have_mxcsr) + jz L2 + pushl $0 + stmxcsr (%esp) + andl $0xFFFFFFC0, (%esp) /* mask out status flags */ + cmpl $0x1F80, (%esp) + popl %esi + jnz invariant_violation +L2: /* otherwise we're OK */ + jmp remove_frame +invariant_violation: + movl $VG_TRC_INVARIANT_FAILED, %eax + movl $0, %edx + +remove_frame: + /* Stash return values */ + movl 28+4(%esp), %edi /* two_words */ + movl %eax, 0(%edi) + movl %edx, 4(%edi) + /* Restore int regs and return. */ + popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + +/*----------------------------------------------------*/ +/*--- Continuation points ---*/ +/*----------------------------------------------------*/ + +/* ------ Chain me to slow entry point ------ */ +.global VG_(disp_cp_chain_me_to_slowEP) +VG_(disp_cp_chain_me_to_slowEP): + /* We got called. The return address indicates + where the patching needs to happen. Collect + the return address and, exit back to C land, + handing the caller the pair (Chain_me_S, RA) */ + movl $VG_TRC_CHAIN_ME_TO_SLOW_EP, %eax + popl %edx + /* 5 = movl $VG_(disp_chain_me_to_slowEP), %edx; + 2 = call *%edx */ + subl $5+2, %edx + jmp postamble + +/* ------ Chain me to fast entry point ------ */ +.global VG_(disp_cp_chain_me_to_fastEP) +VG_(disp_cp_chain_me_to_fastEP): + /* We got called. The return address indicates + where the patching needs to happen. Collect + the return address and, exit back to C land, + handing the caller the pair (Chain_me_F, RA) */ + movl $VG_TRC_CHAIN_ME_TO_FAST_EP, %eax + popl %edx + /* 5 = movl $VG_(disp_chain_me_to_fastEP), %edx; + 2 = call *%edx */ + subl $5+2, %edx + jmp postamble + +/* ------ Indirect but boring jump ------ */ +.global VG_(disp_cp_xindir) +VG_(disp_cp_xindir): + /* Where are we going? */ + movl OFFSET_x86_EIP(%ebp), %eax + + /* stats only */ + addl $1, VG_(stats__n_xIndirs_32) + + /* try a fast lookup in the translation cache */ + movl %eax, %ebx /* next guest addr */ + andl $VG_TT_FAST_MASK, %ebx /* entry# */ + movl 0+VG_(tt_fast)(,%ebx,8), %esi /* .guest */ + movl 4+VG_(tt_fast)(,%ebx,8), %edi /* .host */ + cmpl %eax, %esi + jnz fast_lookup_failed + + /* Found a match. Jump to .host. */ + jmp *%edi + ud2 /* persuade insn decoders not to speculate past here */ + +fast_lookup_failed: + /* stats only */ + addl $1, VG_(stats__n_xIndir_misses_32) + + movl $VG_TRC_INNER_FASTMISS, %eax + movl $0, %edx + jmp postamble + +/* ------ Assisted jump ------ */ +.global VG_(disp_cp_xassisted) +VG_(disp_cp_xassisted): + /* %ebp contains the TRC */ + movl %ebp, %eax + movl $0, %edx + jmp postamble + +/* ------ Event check failed ------ */ +.global VG_(disp_cp_evcheck_fail) +VG_(disp_cp_evcheck_fail): + movl $VG_TRC_INNER_COUNTERZERO, %eax + movl $0, %edx + jmp postamble + + +.size VG_(disp_run_translations), .-VG_(disp_run_translations) + +#endif // defined(VGP_x86_freebsd) + +/* Let the linker know we don't need an executable stack */ +MARK_STACK_NO_EXEC + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_initimg/initimg-freebsd.c b/coregrind/m_initimg/initimg-freebsd.c new file mode 100644 index 0000000000..d19186a42c --- /dev/null +++ b/coregrind/m_initimg/initimg-freebsd.c @@ -0,0 +1,979 @@ + +/*--------------------------------------------------------------------*/ +/*--- Startup: create initial process image on FreeBSD ---*/ +/*--- initimg-freebsd.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + js...@ac... + Copyright (C) 2018-2021 Paul Floyd + pj...@wa... + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + The GNU General Public License is contained in the file COPYING. +*/ + +#if defined(VGO_freebsd) + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_debuglog.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcprint.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_mallocfree.h" +#include "pub_core_machine.h" +#include "pub_core_ume.h" +#include "pub_core_options.h" +#include "pub_core_syscall.h" +#include "pub_core_tooliface.h" /* VG_TRACK */ +#include "pub_core_threadstate.h" /* ThreadArchState */ +#include "pub_core_pathscan.h" +#include "pub_core_initimg.h" /* self */ + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +/* This is for ELF types etc, and also the AT_ constants. */ +#include <elf.h> +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + + +/*====================================================================*/ +/*=== Loading the client ===*/ +/*====================================================================*/ + +/* Load the client whose name is VG_(argv_the_exename). */ + +static void load_client ( /*OUT*/ExeInfo* info, + /*OUT*/Addr* client_ip, + /*OUT*/Addr* client_toc) +{ + const HChar* exe_name; + Int ret; + SysRes res; + + vg_assert( VG_(args_the_exename) != NULL); + exe_name = VG_(find_executable)( VG_(args_the_exename) ); + + if (!exe_name) { + VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename)); + VG_(exit)(127); // 127 is Posix NOTFOUND + } + + VG_(memset)(info, 0, sizeof(*info)); + ret = VG_(do_exec)(exe_name, info); + if (ret < 0) { + VG_(printf)("valgrind: could not execute '%s'\n", exe_name); + VG_(exit)(1); + } + + // The client was successfully loaded! Continue. + + /* Get hold of a file descriptor which refers to the client + executable. This is needed for attaching to GDB. */ + res = VG_(open)(exe_name, VKI_O_RDONLY, VKI_S_IRUSR); + if (!sr_isError(res)) + VG_(cl_exec_fd) = sr_Res(res); + + /* Copy necessary bits of 'info' that were filled in */ + *client_ip = info->init_ip; + *client_toc = info->init_toc; + VG_(brk_base) = VG_(brk_limit) = VG_PGROUNDUP(info->brkbase); +} + + +/*====================================================================*/ +/*=== Setting up the client's environment ===*/ +/*====================================================================*/ + +/* Prepare the client's environment. This is basically a copy of our + environment, except: + + LD_PRELOAD=$VALGRIND_LIB/vgpreload_core-PLATFORM.so: + ($VALGRIND_LIB/vgpreload_TOOL-PLATFORM.so:)? + $LD_PRELOAD + + If this is missing, then it is added. + + Also, remove any binding for VALGRIND_LAUNCHER=. The client should + not be able to see this. + + If this needs to handle any more variables it should be hacked + into something table driven. The copy is VG_(malloc)'d space. +*/ +static HChar** setup_client_env ( HChar** origenv, const HChar* toolname) +{ + vg_assert(origenv); + vg_assert(toolname); + + const HChar* preload_core = "vgpreload_core"; + const HChar* ld_preload = "LD_PRELOAD="; + const HChar* v_launcher = VALGRIND_LAUNCHER "="; + Int ld_preload_len = VG_(strlen)( ld_preload ); + Int v_launcher_len = VG_(strlen)( v_launcher ); + Bool ld_preload_done = False; +#if defined(VGP_x86_freebsd) + const HChar* ld_32_preload = "LD_32_PRELOAD="; + Int ld_32_preload_len = VG_(strlen)( ld_32_preload ); + Bool ld_32_preload_done = False; +#endif + Int vglib_len = VG_(strlen)(VG_(libdir)); + Bool debug = False; + + HChar** cpp; + HChar** ret; + HChar* preload_tool_path; + Int envc, i; + + /* Alloc space for the vgpreload_core.so path and vgpreload_<tool>.so + paths. We might not need the space for vgpreload_<tool>.so, but it + doesn't hurt to over-allocate briefly. The 16s are just cautious + slop. */ + Int preload_core_path_len = vglib_len + sizeof(preload_core) + + sizeof(VG_PLATFORM) + 16; + Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname) + + sizeof(VG_PLATFORM) + 16; + Int preload_string_len = preload_core_path_len + preload_tool_path_len; + HChar* preload_string = VG_(malloc)("initimg-freebsd.sce.1", + preload_string_len); + /* Determine if there's a vgpreload_<tool>_<platform>.so file, and setup + preload_string. */ + preload_tool_path = VG_(malloc)("initimg-freebsd.sce.2", preload_tool_path_len); + VG_(snprintf)(preload_tool_path, preload_tool_path_len, + "%s/vgpreload_%s-%s.so", VG_(libdir), toolname, VG_PLATFORM); + if (VG_(access)(preload_tool_path, True/*r*/, False/*w*/, False/*x*/) == 0) { + VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so:%s", + VG_(libdir), preload_core, VG_PLATFORM, preload_tool_path); + } else { + VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so", + VG_(libdir), preload_core, VG_PLATFORM); + } + VG_(free)(preload_tool_path); + + VG_(debugLog)(2, "initimg", "preload_string:\n"); + VG_(debugLog)(2, "initimg", " \"%s\"\n", preload_string); + + /* Count the original size of the env */ + if (debug) VG_(printf)("\n\n"); + envc = 0; + for (cpp = origenv; cpp && *cpp; cpp++) { + envc++; + if (debug) VG_(printf)("XXXXXXXXX: BEFORE %s\n", *cpp); + } + + /* Allocate a new space */ + ret = VG_(malloc) ("initimg-freebsd.sce.3", + sizeof(HChar *) * (envc+2+1)); /* 2 new entries + NULL */ + + /* copy it over */ + for (cpp = ret; *origenv; ) { + if (debug) VG_(printf)("XXXXXXXXX: COPY %s\n", *origenv); + *cpp++ = *origenv++; + } + *cpp = NULL; + *(cpp + 1) = NULL; + + vg_assert(envc == (cpp - ret)); + + /* Walk over the new environment, mashing as we go */ + for (cpp = ret; cpp && *cpp; cpp++) { + if (VG_(memcmp)(*cpp, ld_preload, ld_preload_len) == 0) { + Int len = VG_(strlen)(*cpp) + preload_string_len; + HChar *cp = VG_(malloc)("initimg-freebsd.sce.4", len); + + VG_(snprintf)(cp, len, "%s%s:%s", + ld_preload, preload_string, (*cpp)+ld_preload_len); + + *cpp = cp; + + ld_preload_done = True; + } + if (debug) VG_(printf)("XXXXXXXXX: MASH %s\n", *cpp); + } + + /* Add the missing bits */ + if (!ld_preload_done) { + Int len = ld_preload_len + preload_string_len; + HChar *cp = VG_(malloc) ("initimg-freebsd.sce.5", len); + + VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string); + + ret[envc++] = cp; + if (debug) VG_(printf)("XXXXXXXXX: ADD %s\n", cp); + } + +#if defined(VGP_x86_freebsd) + /* If we're running a 32 bit binary, ld-elf32.so.1 may be looking for + * a different variable name. Or it might be a 32 bit ld-elf.so.1 in a + * chroot. Cover both cases. */ + if (VG_(is32on64)()) { + for (cpp = ret; cpp && *cpp; cpp++) { + if (VG_(memcmp)(*cpp, ld_32_preload, ld_32_preload_len) == 0) { + Int len = VG_(strlen)(*cpp) + preload_string_len; + HChar *cp = VG_(malloc)("initimg-freebsd.sce.4a", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s:%s", + ld_32_preload, preload_string, (*cpp)+ld_32_preload_len); + + *cpp = cp; + + ld_32_preload_done = True; + } + } + if (!ld_32_preload_done) { + Int len = ld_32_preload_len + preload_string_len; + HChar *cp = VG_(malloc) ("initimg-freebsd.sce.5a", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s", ld_32_preload, preload_string); + + ret[envc++] = cp; + } + } +#endif + + /* ret[0 .. envc-1] is live now. */ + /* Find and remove a binding for VALGRIND_LAUNCHER. */ + for (i = 0; i < envc; i++) + if (0 == VG_(memcmp)(ret[i], v_launcher, v_launcher_len)) + break; + + if (i < envc) { + for (; i < envc-1; i++) + ret[i] = ret[i+1]; + envc--; + } + + VG_(free)(preload_string); + ret[envc] = NULL; + + for (i = 0; i < envc; i++) { + if (debug) VG_(printf)("XXXXXXXXX: FINAL %s\n", ret[i]); + } + + return ret; +} + + +/*====================================================================*/ +/*=== Setting up the client's stack ===*/ +/*====================================================================*/ + +/* Add a string onto the string table, and return its address */ +static HChar *copy_str(HChar **tab, const HChar *str) +{ + HChar *cp = *tab; + HChar *orig = cp; + + while(*str) + *cp++ = *str++; + *cp++ = '\0'; + + if (0) + VG_(printf)("copied %p \"%s\" len %lld\n", (void*)orig, orig, (Long)(cp-orig)); + + *tab = cp; + + return orig; +} + + +/* ---------------------------------------------------------------- + + This sets up the client's initial stack, containing the args, + environment and aux vector. + + The format of the stack is: + + higher address +-----------------+ <- clstack_end + | | + : string table : + | | + +-----------------+ + | AT_NULL | + - - + | auxv | + +-----------------+ + | NULL | + - - + | envp | + +-----------------+ + | NULL | + - - + | argv | + +-----------------+ + | argc | + lower address +-----------------+ <- sp + | undefined | + : : + + Allocate and create the initial client stack. It is allocated down + from clstack_end, which was previously determined by the address + space manager. The returned value is the SP value for the client. + + The client's auxv is created by copying and modifying our own one. + + ---------------------------------------------------------------- */ + +struct auxv { + Word a_type; + union { + void *a_ptr; + Word a_val; + } u; +}; + +static +struct auxv *find_auxv(UWord* sp) +{ + sp++; // skip argc (Nb: is word-sized, not int-sized!) + + while (*sp != 0) // skip argv + sp++; + sp++; + + while (*sp != 0) // skip env + sp++; + sp++; + + return (struct auxv *)sp; +} + +static +Addr setup_client_stack( void* init_sp, + HChar** orig_envp, + const ExeInfo* info, + UInt** client_auxv, + Addr clstack_end, + SizeT clstack_max_size ) +{ + SysRes res; + HChar **cpp; + HChar *strtab; /* string table */ + HChar *stringbase; + Addr *ptr; + struct auxv *auxv; + const struct auxv *orig_auxv; + const struct auxv *cauxv; + unsigned stringsize; /* total size of strings in bytes */ + unsigned auxsize; /* total size of auxv in bytes */ + Int argc; /* total argc */ + Int envc; /* total number of env vars */ + unsigned stacksize; /* total client stack size */ + Addr client_SP; /* client stack base (initial SP) */ + Addr clstack_start; + Int i; + Bool have_exename; + + vg_assert(VG_IS_PAGE_ALIGNED(clstack_end+1)); + vg_assert( VG_(args_for_client) ); + + /* use our own auxv as a prototype */ + orig_auxv = find_auxv(init_sp); + + /* ==================== compute sizes ==================== */ + + /* first of all, work out how big the client stack will be */ + stringsize = 0; + have_exename = VG_(args_the_exename) != NULL; + + /* paste on the extra args if the loader needs them (ie, the #! + interpreter and its argument) */ + argc = 0; + if (info->interp_name != NULL) { + argc++; + stringsize += VG_(strlen)(info->interp_name) + 1; + } + if (info->interp_args != NULL) { + argc++; + stringsize += VG_(strlen)(info->interp_args) + 1; + } + + /* now scan the args we're given... */ + if (have_exename) + stringsize += VG_(strlen)( VG_(args_the_exename) ) + 1; + + for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) { + argc++; + stringsize += VG_(strlen)( * (HChar**) + VG_(indexXA)( VG_(args_for_client), i )) + + 1; + } + + /* ...and the environment */ + envc = 0; + for (cpp = orig_envp; cpp && *cpp; cpp++) { + envc++; + stringsize += VG_(strlen)(*cpp) + 1; + } + + /* now, how big is the auxv? */ + auxsize = sizeof(*auxv); /* there's always at least one entry: AT_NULL */ + for (cauxv = orig_auxv; cauxv->a_type != AT_NULL; cauxv++) { + auxsize += sizeof(*cauxv); + } + + /* OK, now we know how big the client stack is */ + stacksize = + sizeof(Word) + /* argc */ + (have_exename ? sizeof(HChar **) : 0) + /* argc[0] == exename */ + sizeof(HChar **)*argc + /* argv */ + sizeof(HChar **) + /* terminal NULL */ + sizeof(HChar **)*envc + /* envp */ + sizeof(HChar **) + /* terminal NULL */ + auxsize + /* auxv */ + VG_ROUNDUP(stringsize, sizeof(Word)); /* strings (aligned) */ + + if (0) VG_(printf)("stacksize = %u\n", stacksize); + + /* client_SP is the client's stack pointer */ + client_SP = clstack_end - stacksize; + client_SP = VG_ROUNDDN(client_SP, 16); /* make stack 16 byte aligned */ + + /* base of the string table (aligned) */ + stringbase = strtab = (HChar *)clstack_end + - VG_ROUNDUP(stringsize, sizeof(int)); + + clstack_start = VG_PGROUNDDN(client_SP); + + /* The max stack size */ + clstack_max_size = VG_PGROUNDUP(clstack_max_size); + + if (0) + VG_(printf)("stringsize=%u auxsize=%u stacksize=%u maxsize=0x%lx\n" + "clstack_start %p\n" + "clstack_end %p\n", + stringsize, auxsize, stacksize, clstack_max_size, + (void*)clstack_start, (void*)clstack_end); + + /* ==================== allocate space ==================== */ + + { + SizeT anon_size = clstack_end - clstack_start + 1; + SizeT resvn_size = clstack_max_size - anon_size; + Addr anon_start = clstack_start; + Addr resvn_start = anon_start - resvn_size; + SizeT inner_HACK = 0; + Bool ok; + + /* So far we've only accounted for space requirements down to the + stack pointer. If this target's ABI requires a redzone below + the stack pointer, we need to allocate an extra page, to + handle the worst case in which the stack pointer is almost at + the bottom of a page, and so there is insufficient room left + over to put the redzone in. In this case the simple thing to + do is allocate an extra page, by shrinking the reservation by + one page and growing the anonymous area by a corresponding + page. */ + vg_assert(VG_STACK_REDZONE_SZB >= 0); + vg_assert(VG_STACK_REDZONE_SZB < VKI_PAGE_SIZE); + if (VG_STACK_REDZONE_SZB > 0) { + vg_assert(resvn_size > VKI_PAGE_SIZE); + resvn_size -= VKI_PAGE_SIZE; + anon_start -= VKI_PAGE_SIZE; + anon_size += VKI_PAGE_SIZE; + } + + vg_assert(VG_IS_PAGE_ALIGNED(anon_size)); + vg_assert(VG_IS_PAGE_ALIGNED(resvn_size)); + vg_assert(VG_IS_PAGE_ALIGNED(anon_start)); + vg_assert(VG_IS_PAGE_ALIGNED(resvn_start)); + vg_assert(resvn_start == clstack_end + 1 - clstack_max_size); + +# ifdef ENABLE_INNER + inner_HACK = 1024*1024; // create 1M non-fault-extending stack +# endif + + if (0) + VG_(printf)("%#lx 0x%lx %#lx 0x%lx\n", + resvn_start, resvn_size, anon_start, anon_size); + + /* Create a shrinkable reservation followed by an anonymous + segment. Together these constitute a growdown stack. */ + res = VG_(mk_SysRes_Error)(0); + ok = VG_(am_create_reservation)( + resvn_start, + resvn_size -inner_HACK, + SmUpper, + anon_size +inner_HACK + ); + if (ok) { + /* allocate a stack - mmap enough space for the stack */ + res = VG_(am_mmap_anon_fixed_client)( + anon_start -inner_HACK, + anon_size +inner_HACK, + VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC + ); + } + if ((!ok) || sr_isError(res)) { + /* Allocation of the stack failed. We have to stop. */ + VG_(printf)("valgrind: " + "I failed to allocate space for the application's stack.\n"); + VG_(printf)("valgrind: " + "This may be the result of a very large --main-stacksize=\n"); + VG_(printf)("valgrind: setting. Cannot continue. Sorry.\n\n"); + VG_(exit)(1); + } + + vg_assert(ok); + vg_assert(!sr_isError(res)); + + /* Record stack extent -- needed for stack-change code. */ + VG_(clstk_start_base) = anon_start -inner_HACK; + VG_(clstk_end) = VG_(clstk_start_base) + anon_size +inner_HACK -1; + + } + + /* ==================== create client stack ==================== */ + + ptr = (Addr*)client_SP; + + /* --- client argc --- */ + *ptr++ = argc + (have_exename ? 1 : 0); + + /* --- client argv --- */ + if (info->interp_name) + *ptr++ = (Addr)copy_str(&strtab, info->interp_name); + if (info->interp_args) + *ptr++ = (Addr)copy_str(&strtab, info->interp_args); + + if (have_exename) + *ptr++ = (Addr)copy_str(&strtab, VG_(args_the_exename)); + + for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) { + *ptr++ = (Addr)copy_str( + &strtab, + * (HChar**) VG_(indexXA)( VG_(args_for_client), i ) + ); + } + *ptr++ = 0; + + /* --- envp --- */ + VG_(client_envp) = (HChar **)ptr; + for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++) + *ptr = (Addr)copy_str(&strtab, *cpp); + *ptr++ = 0; + + /* --- auxv --- */ + auxv = (struct auxv *)ptr; + *client_auxv = (UInt *)auxv; +#if defined(VGP_x86_freebsd) + int* pagesizes = NULL; +#endif + + /* + * The PAGESIZES hack - PJF + * + * Normally a standalone application has a full auxv which, among + * many other things contains a vector of integers (PAGESIZES) + * of a platform dependent length (PAGESIZESLEN). On x86 the + * length is 2, on amd64 the length is 3 (there are other lengths + * for architectures not supported on Valgrind). + * + * When the dynamic loader executes it will run a routine + * static void init_pagesizes(Elf_Auxinfo **aux_info) + * (see /usr/src/libexec/rtld-elf/rltd.c). If the PAGESIZES info is in + * auxv, init_pagesizes will use that. However, normally this loop + * does not copy 'pointered' elements (because that would generate + * 'Invalid reads' in the guest). This means that the auxv that + * Valgrind provides to ldrt *doesn't* normally contain + * PAGESIZES. + * + * So init_pagesizes falls back to using sysctlnametomib/sysctl + * to read "hw.pagesizes". Unfortunately there seems to be a bug + * in this for an x86 executable compiled and running on an amd64 + * kernel. + * + * The application sees MAXPAGESLEN as 3 (from the amd64 headers) + * but the x86 kernel sees MAXPAGESLEN as 2. The routine that + * copies out the data for a sysctl sees this discrepancy and + * sets an ENOMEM error. So guest execution doesn't even get past + * executing the dynamic linker. + */ + + for (; orig_auxv->a_type != AT_NULL; auxv++, orig_auxv++) { + + /* copy the entry... */ + *auxv = *orig_auxv; + + /* + * ...and fix up / examine the copy + * in general there are thee possibilities for these items + * 1. copy it, a common case for scalars + * 2. synthesize, if the value that the host gets isn't what we want + * 3. ignore, usually the case for pointers to memory for the host + * the ignored items are just left commented out + */ + switch(auxv->a_type) { + + case AT_IGNORE: + case AT_PHENT: + case AT_PAGESZ: + case AT_FLAGS: + case AT_NOTELF: + case AT_UID: + case AT_EUID: + case AT_GID: + case AT_EGID: + case AT_STACKPROT: + case AT_NCPUS: + case AT_OSRELDATE: +#if (FREEBSD_VERS >= FREEBSD_11) + // FreeBSD 11+ also have HWCAP and HWCAP2 + case AT_EHDRFLAGS: +#endif + /* All these are pointerless, so we don't need to do + anything about them. */ + break; + // case AT_CANARYLEN: + // case AT_EXECPATH: + // case AT_CANARY: +#if defined(VGP_x86_freebsd) + case AT_PAGESIZESLEN: + if (!VG_(is32on64)()) { + VG_(debugLog)(2, "initimg", + "stomping auxv entry %llu\n", + (ULong)auxv->a_type); + auxv->a_type = AT_IGNORE; + } + break; + case AT_PAGESIZES: + if (VG_(is32on64)()) { + pagesizes = VG_(malloc)("initimg-freebsd.cpauxv.1", 2*sizeof(int)); + ... [truncated message content] |