You can subscribe to this list here.
| 2002 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
(122) |
Nov
(152) |
Dec
(69) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2003 |
Jan
(6) |
Feb
(25) |
Mar
(73) |
Apr
(82) |
May
(24) |
Jun
(25) |
Jul
(10) |
Aug
(11) |
Sep
(10) |
Oct
(54) |
Nov
(203) |
Dec
(182) |
| 2004 |
Jan
(307) |
Feb
(305) |
Mar
(430) |
Apr
(312) |
May
(187) |
Jun
(342) |
Jul
(487) |
Aug
(637) |
Sep
(336) |
Oct
(373) |
Nov
(441) |
Dec
(210) |
| 2005 |
Jan
(385) |
Feb
(480) |
Mar
(636) |
Apr
(544) |
May
(679) |
Jun
(625) |
Jul
(810) |
Aug
(838) |
Sep
(634) |
Oct
(521) |
Nov
(965) |
Dec
(543) |
| 2006 |
Jan
(494) |
Feb
(431) |
Mar
(546) |
Apr
(411) |
May
(406) |
Jun
(322) |
Jul
(256) |
Aug
(401) |
Sep
(345) |
Oct
(542) |
Nov
(308) |
Dec
(481) |
| 2007 |
Jan
(427) |
Feb
(326) |
Mar
(367) |
Apr
(255) |
May
(244) |
Jun
(204) |
Jul
(223) |
Aug
(231) |
Sep
(354) |
Oct
(374) |
Nov
(497) |
Dec
(362) |
| 2008 |
Jan
(322) |
Feb
(482) |
Mar
(658) |
Apr
(422) |
May
(476) |
Jun
(396) |
Jul
(455) |
Aug
(267) |
Sep
(280) |
Oct
(253) |
Nov
(232) |
Dec
(304) |
| 2009 |
Jan
(486) |
Feb
(470) |
Mar
(458) |
Apr
(423) |
May
(696) |
Jun
(461) |
Jul
(551) |
Aug
(575) |
Sep
(134) |
Oct
(110) |
Nov
(157) |
Dec
(102) |
| 2010 |
Jan
(226) |
Feb
(86) |
Mar
(147) |
Apr
(117) |
May
(107) |
Jun
(203) |
Jul
(193) |
Aug
(238) |
Sep
(300) |
Oct
(246) |
Nov
(23) |
Dec
(75) |
| 2011 |
Jan
(133) |
Feb
(195) |
Mar
(315) |
Apr
(200) |
May
(267) |
Jun
(293) |
Jul
(353) |
Aug
(237) |
Sep
(278) |
Oct
(611) |
Nov
(274) |
Dec
(260) |
| 2012 |
Jan
(303) |
Feb
(391) |
Mar
(417) |
Apr
(441) |
May
(488) |
Jun
(655) |
Jul
(590) |
Aug
(610) |
Sep
(526) |
Oct
(478) |
Nov
(359) |
Dec
(372) |
| 2013 |
Jan
(467) |
Feb
(226) |
Mar
(391) |
Apr
(281) |
May
(299) |
Jun
(252) |
Jul
(311) |
Aug
(352) |
Sep
(481) |
Oct
(571) |
Nov
(222) |
Dec
(231) |
| 2014 |
Jan
(185) |
Feb
(329) |
Mar
(245) |
Apr
(238) |
May
(281) |
Jun
(399) |
Jul
(382) |
Aug
(500) |
Sep
(579) |
Oct
(435) |
Nov
(487) |
Dec
(256) |
| 2015 |
Jan
(338) |
Feb
(357) |
Mar
(330) |
Apr
(294) |
May
(191) |
Jun
(108) |
Jul
(142) |
Aug
(261) |
Sep
(190) |
Oct
(54) |
Nov
(83) |
Dec
(22) |
| 2016 |
Jan
(49) |
Feb
(89) |
Mar
(33) |
Apr
(50) |
May
(27) |
Jun
(34) |
Jul
(53) |
Aug
(53) |
Sep
(98) |
Oct
(206) |
Nov
(93) |
Dec
(53) |
| 2017 |
Jan
(65) |
Feb
(82) |
Mar
(102) |
Apr
(86) |
May
(187) |
Jun
(67) |
Jul
(23) |
Aug
(93) |
Sep
(65) |
Oct
(45) |
Nov
(35) |
Dec
(17) |
| 2018 |
Jan
(26) |
Feb
(35) |
Mar
(38) |
Apr
(32) |
May
(8) |
Jun
(43) |
Jul
(27) |
Aug
(30) |
Sep
(43) |
Oct
(42) |
Nov
(38) |
Dec
(67) |
| 2019 |
Jan
(32) |
Feb
(37) |
Mar
(53) |
Apr
(64) |
May
(49) |
Jun
(18) |
Jul
(14) |
Aug
(53) |
Sep
(25) |
Oct
(30) |
Nov
(49) |
Dec
(31) |
| 2020 |
Jan
(87) |
Feb
(45) |
Mar
(37) |
Apr
(51) |
May
(99) |
Jun
(36) |
Jul
(11) |
Aug
(14) |
Sep
(20) |
Oct
(24) |
Nov
(40) |
Dec
(23) |
| 2021 |
Jan
(14) |
Feb
(53) |
Mar
(85) |
Apr
(15) |
May
(19) |
Jun
(3) |
Jul
(14) |
Aug
(1) |
Sep
(57) |
Oct
(73) |
Nov
(56) |
Dec
(22) |
| 2022 |
Jan
(3) |
Feb
(22) |
Mar
(6) |
Apr
(55) |
May
(46) |
Jun
(39) |
Jul
(15) |
Aug
(9) |
Sep
(11) |
Oct
(34) |
Nov
(20) |
Dec
(36) |
| 2023 |
Jan
(79) |
Feb
(41) |
Mar
(99) |
Apr
(169) |
May
(48) |
Jun
(16) |
Jul
(16) |
Aug
(57) |
Sep
(19) |
Oct
|
Nov
|
Dec
|
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
|
|
|
|
1
(2) |
2
(2) |
3
|
4
|
|
5
|
6
(3) |
7
(1) |
8
(1) |
9
(4) |
10
(1) |
11
(1) |
|
12
(2) |
13
(7) |
14
(3) |
15
(2) |
16
(7) |
17
(1) |
18
(2) |
|
19
|
20
|
21
|
22
|
23
|
24
|
25
|
|
26
|
27
|
28
|
29
|
30
|
|
|
|
From: Paul F. <pa...@so...> - 2022-06-14 20:47:36
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=a108669a184a7bb93324c2a193b14245cad7365d commit a108669a184a7bb93324c2a193b14245cad7365d Author: Paul Floyd <pj...@wa...> Date: Tue Jun 14 22:39:31 2022 +0200 Implement vgdb invoker on FreeBSD This is a translation of the Linux vgdb-invoker-ptrace.c to the FreeBSD ptrace dialect. It seems to be basically functional (3 out of 4 of the regression tests pass, and for the 4th one it seems to be a limitation of ptrace on FreeBSD that it can cause syscalls to be interrupted). Diff: --- NEWS | 1 + coregrind/Makefile.am | 5 +- coregrind/vgdb-invoker-freebsd.c | 620 +++++++++++++++++++++++++++++++++++++++ include/vki/vki-freebsd.h | 49 ++-- 4 files changed, 639 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index 1f92c25e3d..eb098b1d07 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,7 @@ are not entered into bugzilla tend to get forgotten about or ignored. 453055 shared_timed_mutex drd test fails with "Lock shared failed" message 453602 Missing command line option to enable/disable debuginfod 452802 Handle lld 9+ split RW PT_LOAD segments correctly +n-i-bz Implement vgdb invoker on FreeBSD To see details of a given bug, visit https://bugs.kde.org/show_bug.cgi?id=XXXXXX diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 76c0aebc9d..151f5c2f00 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -98,10 +98,7 @@ if VGCONF_OS_IS_SOLARIS vgdb_SOURCES += vgdb-invoker-solaris.c endif if VGCONF_OS_IS_FREEBSD -# As with Darwin, we don't have ptrace PTRACE_PEEKTEXT -# so we can't use vgdb-invoker-ptrace.c -# Need to find an alternative, like Solaris -vgdb_SOURCES += vgdb-invoker-none.c +vgdb_SOURCES += vgdb-invoker-freebsd.c endif vgdb_CPPFLAGS = $(AM_CPPFLAGS_PRI) diff --git a/coregrind/vgdb-invoker-freebsd.c b/coregrind/vgdb-invoker-freebsd.c new file mode 100644 index 0000000000..2350de8caa --- /dev/null +++ b/coregrind/vgdb-invoker-freebsd.c @@ -0,0 +1,620 @@ +/*--------------------------------------------------------------------*/ +/*--- Implementation of vgdb invoker subsystem via ptrace() calls. ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2011-2017 Philippe Waroquiers + Copyright (C) 2021-2022 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 "config.h" + +#include "vgdb.h" +#include "pub_core_threadstate.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <sys/time.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <sys/uio.h> +#include <elf.h> +#include <sys/procfs.h> + +/* + * This file is largely a copy of vgdb-invoker-ptrace.c + * + * Sadly, though ptrace exists on most unix-like systems, + * no two versions are the same. + * + * The main two differences are that + * - FreeBSD ptrace works at the process level, not lwps + * so just attaching to the main pid stops everything + * (no need to read memory to get lwpids and stop all of the + * threads) + * - Reading registers is a lot simpler. + * + * Known limitations: + * gdbserver_tests/nlcontrolc has different behaviour + * becase attaching causes the select() syscall of main + * to be interrupted. This seems to be a "feature" of + * ptrace on FreeBSD so I doubt it can be fixed. + * + */ + +// 32-bit or 64-bit wide, depending on primary architecture. +typedef Addr CORE_ADDR; +typedef int PTRACE_XFER_TYPE; +typedef caddr_t PTRACE_ARG3_TYPE; + +// if > 0, pid for which registers have to be restored. +// if == 0, means we have not yet called setregs (or have already +// restored the registers). +static int pid_of_save_regs = 0; +/* True if we have continued pid_of_save_regs after PT_ATTACH. */ +static Bool pid_of_save_regs_continued = False; +// When setregs has been called to change the registers of pid_of_save_regs, +// vgdb cannot transmit the signals intercepted during ptrace. +// So, we queue them, and will deliver them when detaching. +// See function waitstopped for more info. +static int signal_queue_sz = 0; +static siginfo_t *signal_queue; + +/* True when loss of connection indicating that the Valgrind + process is dying. */ +static Bool dying = False; + +/* Copy LEN bytes of data from vgdb memory at MYADDR + to valgrind memory at MEMADDR. + On failure (cannot write the valgrind memory) + returns the value of errno. */ +__attribute__((unused)) /* not used on all platforms */ +static +int ptrace_write_memory (pid_t inferior_pid, CORE_ADDR memaddr, + const void *myaddr, size_t len) +{ + size_t i; + /* Round starting address down to longword boundary. */ + CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + size_t count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) + / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + PTRACE_XFER_TYPE *buffer + = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + + if (debuglevel >= 1) { + DEBUG (1, "Writing "); + for (i = 0; i < len; i++) + PDEBUG (1, "%02x", ((const unsigned char*)myaddr)[i]); + PDEBUG(1, " to %p\n", (void *) memaddr); + } + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (PT_READ_I, inferior_pid, + (PTRACE_ARG3_TYPE) addr, 0); + + if (count > 1) { + buffer[count - 1] + = ptrace (PT_READ_I, inferior_pid, + (PTRACE_ARG3_TYPE) (addr + (count - 1) + * sizeof (PTRACE_XFER_TYPE)), + 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), + myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { + errno = 0; + ptrace (PT_WRITE_I, inferior_pid, + (PTRACE_ARG3_TYPE) addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +static +char *status_image (int status) +{ + static char result[256]; // large enough + int sz = 0; +#define APPEND(...) sz += snprintf (result+sz, 256 - sz - 1, __VA_ARGS__) + + result[0] = 0; + + if (WIFEXITED(status)) + APPEND ("WIFEXITED %d ", WEXITSTATUS(status)); + + if (WIFSIGNALED(status)) { + APPEND ("WIFSIGNALED %d ", WTERMSIG(status)); + if (WCOREDUMP(status)) APPEND ("WCOREDUMP "); + } + + if (WIFSTOPPED(status)) + APPEND ("WIFSTOPPED %d ", WSTOPSIG(status)); + +#ifdef WIFCONTINUED + if (WIFCONTINUED(status)) + APPEND ("WIFCONTINUED "); +#endif + + return result; +#undef APPEND +} + +/* Wait till the process pid is reported as stopped with signal_expected. + If other signal(s) than signal_expected are received, waitstopped + will pass them to pid, waiting for signal_expected to stop pid. + Returns True when process is in stopped state with signal_expected. + Returns False if a problem was encountered while waiting for pid + to be stopped. + + If pid is reported as being dead/exited, waitstopped will return False. +*/ +static +Bool waitstopped (pid_t pid, int signal_expected, const char *msg) +{ + pid_t p; + int status = 0; + int signal_received; + int res; + + while (1) { + DEBUG(1, "waitstopped %s before waitpid signal_expected %d\n", + msg, signal_expected); + p = waitpid(pid, &status, 0); /* PJF options was __WALL */ + DEBUG(1, "after waitpid pid %d p %d status 0x%x %s\n", pid, p, + status, status_image (status)); + if (p != pid) { + ERROR(errno, "%s waitpid pid %d in waitstopped %d status 0x%x %s\n", + msg, pid, p, status, status_image (status)); + return False; + } + + /* The process either exited or was terminated by a (fatal) signal. */ + if (WIFEXITED(status) || WIFSIGNALED(status)) { + shutting_down = True; + return False; + } + + assert (WIFSTOPPED(status)); + signal_received = WSTOPSIG(status); + if (signal_received == signal_expected) + break; + + /* pid received a signal which is not the signal we are waiting for. + If we have not (yet) changed the registers of the inferior + or we have (already) reset them, we can transmit the signal. + + If we have already set the registers of the inferior, we cannot + transmit the signal, as this signal would arrive when the + gdbserver code runs. And valgrind only expects signals to + arrive in a small code portion around + client syscall logic, where signal are unmasked (see e.g. + m_syswrap/syscall-x86-linux.S ML_(do_syscall_for_client_WRK). + + As ptrace is forcing a call to gdbserver by jumping + 'out of this region', signals are not masked, but + will arrive outside of the allowed/expected code region. + So, if we have changed the registers of the inferior, we + rather queue the signal to transmit them when detaching, + after having restored the registers to the initial values. */ + if (pid_of_save_regs) { + siginfo_t *newsiginfo; + struct ptrace_lwpinfo new_lwpinfo; + + // realloc a bigger queue, and store new signal at the end. + // This is not very efficient but we assume not many sigs are queued. + if (signal_queue_sz >= 64) { + DEBUG(0, "too many queued signals while waiting for SIGSTOP\n"); + return False; + } + signal_queue_sz++; + signal_queue = vrealloc(signal_queue, + sizeof(siginfo_t) * signal_queue_sz); + newsiginfo = signal_queue + (signal_queue_sz - 1); + + res = ptrace (PT_LWPINFO, pid, (caddr_t)&new_lwpinfo, sizeof(new_lwpinfo)); + *newsiginfo = new_lwpinfo.pl_siginfo; + if (res != 0) { + ERROR(errno, "PT_LWPINFO failed: signal lost !!!!\n"); + signal_queue_sz--; + } else + DEBUG(1, "waitstopped PTRACE_CONT, queuing signal %d" + " si_signo %d si_pid %d\n", + signal_received, newsiginfo->si_signo, newsiginfo->si_pid); + res = ptrace (PT_CONTINUE, pid, (caddr_t)1, 0); + } else { + DEBUG(1, "waitstopped PT_CONTINUE with signal %d\n", signal_received); + res = ptrace (PT_CONTINUE, pid, (caddr_t)1, signal_received); + } + if (res != 0) { + ERROR(errno, "waitstopped PTRACE_CONT\n"); + return False; + } + } + + return True; +} + +/* Stops the given pid, wait for the process to be stopped. + Returns True if successful, False otherwise. + msg is used in tracing and error reporting. */ +static +Bool stop (pid_t pid, const char *msg) +{ + long res; + + DEBUG(1, "%s SIGSTOP pid %d\n", msg, pid); + res = kill (pid, SIGSTOP); + if (res != 0) { + ERROR(errno, "%s SIGSTOP pid %d %ld\n", msg, pid, res); + return False; + } + + return waitstopped (pid, SIGSTOP, msg); + +} + +/* Attaches to given pid, wait for the process to be stopped. + Returns True if successful, False otherwise. + msg is used in tracing and error reporting. */ +static +Bool attach (pid_t pid, const char *msg) +{ + int res; + static Bool output_error = True; + static Bool initial_attach = True; + // For a ptrace_scope protected system, we do not want to output + // repetitively attach error. We will output once an error + // for the initial_attach. Once the 1st attach has succeeded, we + // again show all errors. + + DEBUG(1, "%s PT_ATTACH pid %d\n", msg, pid); + res = ptrace (PT_ATTACH, pid, 0, 0); + if (res != 0) { + if (output_error || debuglevel > 0) { + ERROR(errno, "%s PT_ATTACH pid %d %ld\n", msg, pid, res); + if (initial_attach) + output_error = False; + } + return False; + } + + initial_attach = False; + output_error = True; + return waitstopped(pid, SIGSTOP, msg); +} + +static +void detach_from_all_threads (pid_t pid) +{ + long res = ptrace (PT_DETACH, pid, NULL, 0); + + if (res != 0) { + ERROR(errno, "PT_DETACH pid %d res %ld\n", + pid, res); + } +} + +static struct reg reg_save; + +/* Get the registers from pid into regs. + Returns True if all ok, otherwise False. */ +static +Bool getregs (pid_t pid, struct reg *regs) +{ + if (ptrace(PT_GETREGS, pid, (caddr_t)regs, 0) < 0) { + return False; + } + return True; +} + +/* Set the registers of pid to regs. + Returns True if all ok, otherwise False. */ +static +Bool setregs (pid_t pid, struct reg *regs) +{ + if (ptrace(PT_SETREGS, pid, (caddr_t)regs, 0) < 0) { + return False; + } + return True; +} + +/* Restore the registers to the saved value, then detaches from all threads */ +static +void restore_and_detach (pid_t pid) +{ + int res; + + DEBUG(1, "restore_and_detach pid %d pid_of_save_regs %d\n", + pid, pid_of_save_regs); + + if (pid_of_save_regs) { + /* In case the 'main pid' has been continued, we need to stop it + before resetting the registers. */ + if (pid_of_save_regs_continued) { + pid_of_save_regs_continued = False; + if (!stop(pid_of_save_regs, "sigstop before reset regs")) + DEBUG(0, "Could not sigstop before reset"); + } + + DEBUG(1, "setregs restore registers pid %d\n", pid_of_save_regs); + if (!setregs(pid_of_save_regs, ®_save)) { + ERROR(errno, "setregs restore registers pid %d after cont\n", + pid_of_save_regs); + } + + /* Now, we transmit all the signals we have queued. */ + if (signal_queue_sz > 0) { + int i; + for (i = 0; i < signal_queue_sz; i++) { + DEBUG(1, "PTRACE_CONT to transmit queued signal %d\n", + signal_queue[i].si_signo); + res = ptrace (PT_CONTINUE, pid_of_save_regs, (caddr_t)1, + signal_queue[i].si_signo); + if (res != 0) + ERROR(errno, "PT_CONTINUE with signal %d\n", + signal_queue[i].si_signo); + if (!stop(pid_of_save_regs, "sigstop after transmit sig")) + DEBUG(0, "Could not sigstop after transmit sig"); + } + free (signal_queue); + signal_queue = NULL; + signal_queue_sz = 0; + } + pid_of_save_regs = 0; + } else { + DEBUG(1, "PTRACE_SETREGS restore registers: no pid\n"); + } + if (signal_queue) + ERROR (0, "One or more signals queued were not delivered. " + "First signal: %d\n", signal_queue[0].si_signo); + detach_from_all_threads(pid); +} + +Bool invoker_invoke_gdbserver (pid_t pid) +{ + long res; + Bool stopped; + struct reg reg_mod; + Addr sp __attribute__((unused)); // Not used on all platforms. + + /* A specific int value is passed to invoke_gdbserver, to check + everything goes according to the plan. */ + const int check = 0x8BADF00D; // ate bad food. + + const Addr bad_return = 0; + // A bad return address will be pushed on the stack. + // The function invoke_gdbserver cannot return. If ever it returns, a NULL + // address pushed on the stack should ensure this is detected. + + /* Not yet attached. If problem, vgdb can abort, + no cleanup needed. */ + + DEBUG(1, "attach to 'main' pid %d\n", pid); + if (!attach(pid, "attach main pid")) { + ERROR(0, "error attach main pid %d\n", pid); + return False; + } + + /* Now, we are attached. If problem, detach and return. */ + + DEBUG(1, "calling getregs\n"); + + if (!getregs(pid, ®_mod)) { + detach_from_all_threads(pid); + return False; + } + reg_save = reg_mod; + + DEBUG(1, "getregs call succeeded\n"); + +#if defined(VGA_x86) + sp = reg_mod.r_esp; +#elif defined(VGA_amd64) + sp = reg_mod.r_rsp; + if (shared32 != NULL) { + /* 64bit vgdb speaking with a 32bit executable. + To have system call restart properly, we need to sign extend rax. + For more info: + web search '[patch] Fix syscall restarts for amd64->i386 biarch' + e.g. http://sourceware.org/ml/gdb-patches/2009-11/msg00592.html */ + *(long *)®_save.r_rax = *(int*)®_save.r_rax; + DEBUG(1, "Sign extending %8.8lx to %8.8lx\n", + reg_mod.r_rax, reg_save.r_rax); + } +#else + I_die_here : (sp) architecture missing in vgdb-invoker-freebsd.c +#endif + + + // the magic below is derived from spying what gdb sends to + // the (classical) gdbserver when invoking a C function. + if (shared32 != NULL) { + // vgdb speaking with a 32bit executable. +#if defined(VGA_x86) || defined(VGA_amd64) + const int regsize = 4; + int rw; + /* push check arg on the stack */ + sp = sp - regsize; + DEBUG(1, "push check arg ptrace_write_memory\n"); + assert(regsize == sizeof(check)); + rw = ptrace_write_memory(pid, sp, + &check, + regsize); + if (rw != 0) { + ERROR(rw, "push check arg ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } + + sp = sp - regsize; + DEBUG(1, "push bad_return return address ptrace_write_memory\n"); + // Note that for a 64 bits vgdb, only 4 bytes of NULL bad_return + // are written. + rw = ptrace_write_memory(pid, sp, + &bad_return, + regsize); + if (rw != 0) { + ERROR(rw, "push bad_return return address ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } +#if defined(VGA_x86) + /* set ebp, esp, eip and orig_eax to invoke gdbserver */ + // compiled in 32bits, speaking with a 32bits exe + reg_mod.r_ebp = sp; // bp set to sp + reg_mod.r_esp = sp; + reg_mod.r_eip = shared32->invoke_gdbserver; +#elif defined(VGA_amd64) + /* set ebp, esp, eip and orig_eax to invoke gdbserver */ + // compiled in 64bits, speaking with a 32bits exe + reg_mod.r_rbp = sp; // bp set to sp + reg_mod.r_rsp = sp; + reg_mod.r_rip = shared32->invoke_gdbserver; +#else + I_die_here : not x86 or amd64 in x86/amd64 section/ +#endif + +#else + I_die_here : architecture missing in vgdb-invoker-freebsd.c +#endif + } + + else if (shared64 != NULL) { +#if defined(VGA_x86) + assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe +#elif defined(VGA_amd64) + // vgdb speaking with a 64 bit executable. + const int regsize = 8; + int rw; + + /* give check arg in rdi */ + reg_mod.r_rdi = check; + + /* push return address on stack : return to breakaddr */ + sp &= ~0xf; // keep the stack aligned on 16 bytes ... + //sp = sp - 128; // do not touch the amd64 redzone + sp = sp - regsize; + DEBUG(1, "push bad_return return address ptrace_write_memory\n"); + rw = ptrace_write_memory(pid, sp, + &bad_return, + sizeof(bad_return)); + if (rw != 0) { + ERROR(rw, "push bad_return return address ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } + + /* set rbp, rsp, rip and orig_rax to invoke gdbserver */ + reg_mod.r_rbp = sp; // bp set to sp + reg_mod.r_rsp = sp; + reg_mod.r_rip = shared64->invoke_gdbserver; + +#else + I_die_here: architecture missing in vgdb-invoker-freebsd.c +#endif + } + else { + assert(0); + } + + DEBUG(1, "calling setregs\n"); + + if (!setregs(pid, ®_mod)) { + detach_from_all_threads(pid); + return False; + } + + DEBUG(1, "setregs succeeded\n"); + + /* Now that we have modified the registers, we set + pid_of_save_regs to indicate that restore_and_detach + must restore the registers in case of cleanup. */ + pid_of_save_regs = pid; + pid_of_save_regs_continued = False; + + + /* We PTRACE_CONT-inue pid. + Either gdbserver will be invoked directly (if all + threads are interruptible) or gdbserver will be + called soon by the scheduler. In the first case, + pid will stop on the break inserted above when + gdbserver returns. In the 2nd case, the break will + be encountered directly. */ + DEBUG(1, "PT_CONTINUE to invoke\n"); + /* an address of 1 means continue without modifying IP + since we already set IP above there is no need to set it here */ + res = ptrace (PT_CONTINUE, pid, (caddr_t)1, 0); + if (res != 0) { + ERROR(errno, "PT_CONTINUE\n"); + restore_and_detach(pid); + return False; + } + pid_of_save_regs_continued = True; + /* Wait for SIGSTOP generated by m_gdbserver.c give_control_back_to_vgdb */ + stopped = waitstopped (pid, SIGSTOP, + "waitpid status after PTRACE_CONT to invoke"); + if (stopped) { + /* Here pid has properly stopped on the break. */ + pid_of_save_regs_continued = False; + restore_and_detach(pid); + return True; + } else { + /* Whatever kind of problem happened. We shutdown. */ + shutting_down = True; + return False; + } +} + +void invoker_cleanup_restore_and_detach(void *v_pid) +{ + DEBUG(1, "invoker_cleanup_restore_and_detach dying: %d\n", dying); + if (!dying) + restore_and_detach(*(int*)v_pid); +} + +void invoker_restrictions_msg(void) +{ +} + +void invoker_valgrind_dying(void) +{ + /* Avoid messing up with registers of valgrind when it is dying. */ + pid_of_save_regs_continued = False; + dying = True; +} diff --git a/include/vki/vki-freebsd.h b/include/vki/vki-freebsd.h index a94fe9477f..ef5b27d6a7 100644 --- a/include/vki/vki-freebsd.h +++ b/include/vki/vki-freebsd.h @@ -40,21 +40,6 @@ is used. The files the code is taken from is indicated. - - Note especially that the types are not the glibc versions, many of which - are different to those in here. - - Also note that this file contains all the generic header info, ie. that - from linux/include/linux/ *.h. The arch-specific header info, eg. that - from linux/include/asm-i386/ *.h, is in vki-$PLATFORM.h and - vki_posixtypes-$PLATFORM.h. (Two files are required to avoid - circular dependencies between the generic VKI header and the - arch-specific VKI header. It's possible in the future, as more stuff - gets pulled in, that we might have to split files up some more to avoid - further circular dependencies.) - - Finally, note that it is assumed that __KERNEL__ is set for all these - definitions, which affects some of them. */ #ifndef VKI_FREEBSD_H @@ -1448,22 +1433,22 @@ union vki_semun { #define VKI_WNOHANG 0x00000001 typedef enum vki_idtype { - P_PID, - P_PPID, - P_PGID, - P_SID, - P_CID, - P_UID, - P_GID, - P_ALL, - P_LWPID, - P_TASKID, - P_PROJID, - P_POOLID, - P_JAILID, - P_CTID, - P_CPUID, - P_PSETID + VKI_P_PID, + VKI_P_PPID, + VKI_P_PGID, + VKI_P_SID, + VKI_P_CID, + VKI_P_UID, + VKI_P_GID, + VKI_P_ALL, + VKI_P_LWPID, + VKI_P_TASKID, + VKI_P_PROJID, + VLI_P_POOLID, + VKI_P_JAILID, + VKI_P_CTID, + VKI_P_CPUID, + VKI_P_PSETID } vki_idtype_t; //---------------------------------------------------------------------- @@ -1535,7 +1520,7 @@ struct vki_dirent { vki_uint16_t d_reclen; vki_uint8_t d_type; vki_uint8_t d_namelen; - char d_name[256]; /* We must not include limits.h! */ + char vki_d_name[256]; /* We must not include limits.h! */ }; //---------------------------------------------------------------------- |
|
From: Mark W. <ma...@so...> - 2022-06-14 19:24:58
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=026cda6c8111ef6ff79bd5c6e083ab50ccf3ded6 commit 026cda6c8111ef6ff79bd5c6e083ab50ccf3ded6 Author: Luboš Luňák <l....@ce...> Date: Tue Apr 26 13:53:14 2022 +0200 support DW_FORM_addrx3 and DW_FORM_strx3 Apparently these may get used after all with large enough binaries, despite being somewhat tricky with regard to endianess. Diff: --- coregrind/m_debuginfo/readdwarf3.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c index 5a031a6043..96bd21f7f7 100644 --- a/coregrind/m_debuginfo/readdwarf3.c +++ b/coregrind/m_debuginfo/readdwarf3.c @@ -314,6 +314,25 @@ static Long get_SLEB128 ( Cursor* c ) { result |= -(1ULL << shift); return result; } +static UInt get_UInt3 ( Cursor* c ) { + UChar c1, c2, c3; + vg_assert(is_sane_Cursor(c)); + if (c->sli_next + 3 > c->sli.ioff + c->sli.szB) { + c->barf(c->barfstr); + /*NOTREACHED*/ + vg_assert(0); + } + c1 = ML_(img_get_UChar)(c->sli.img, c->sli_next); + c2 = ML_(img_get_UChar)(c->sli.img, c->sli_next+1); + c3 = ML_(img_get_UChar)(c->sli.img, c->sli_next+2); + c->sli_next += 3; +#if defined(VG_BIGENDIAN) + return c1 << 16 | c2 << 8 | c3; +#else + return c1 | c2 << 8 | c3 << 16; +#endif +} + /* Assume 'c' points to the start of a string. Return a DiCursor of whatever it points at, and advance it past the terminating zero. @@ -1846,6 +1865,12 @@ void get_Form_contents ( /*OUT*/FormContents* cts, get_Form_contents_addr(cts, form, index, cc, td3); break; } + case DW_FORM_addrx3: { + /* this is an offset into .debug_addr */ + ULong index = (ULong)get_UInt3(c); + get_Form_contents_addr(cts, form, index, cc, td3); + break; + } case DW_FORM_addrx4: { /* this is an offset into .debug_addr */ ULong index = (ULong)get_UInt(c); @@ -1870,6 +1895,12 @@ void get_Form_contents ( /*OUT*/FormContents* cts, get_Form_contents_str_offsets(cts, form, index, cc, td3); break; } + case DW_FORM_strx3: { + /* this is an offset into .debug_str_offsets */ + ULong index = (ULong)get_UInt3(c); + get_Form_contents_str_offsets(cts, form, index, cc, td3); + break; + } case DW_FORM_strx4: { /* this is an offset into .debug_str_offsets */ ULong index = (ULong)get_UInt(c); |
|
From: Mark W. <ma...@so...> - 2022-06-14 19:09:33
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=4bb0164e6b2067376f085452a40137f13436384c commit 4bb0164e6b2067376f085452a40137f13436384c Author: Luboš Luňák <l....@ce...> Date: Mon Apr 25 22:11:27 2022 +0200 implement support for missing DW_LLE_* and DW_RLE_* values Diff: --- coregrind/m_debuginfo/d3basics.c | 31 ++++++++++++ coregrind/m_debuginfo/priv_d3basics.h | 2 + coregrind/m_debuginfo/readdwarf3.c | 93 ++++++++++++++++++++++++++++------- 3 files changed, 109 insertions(+), 17 deletions(-) diff --git a/coregrind/m_debuginfo/d3basics.c b/coregrind/m_debuginfo/d3basics.c index 555e1e00d0..d48589a30c 100644 --- a/coregrind/m_debuginfo/d3basics.c +++ b/coregrind/m_debuginfo/d3basics.c @@ -435,6 +435,37 @@ const HChar* ML_(pp_DW_AT) ( DW_AT attr ) return "DW_AT_???"; } +const HChar* ML_(pp_DW_LLE) ( DW_LLE entry ) +{ + switch (entry) { + case DW_LLE_end_of_list: return "DW_LLE_end_of_list"; + case DW_LLE_base_addressx: return "DW_LLE_base_addressx"; + case DW_LLE_startx_endx: return "DW_LLE_startx_endx"; + case DW_LLE_startx_length: return "DW_LLE_startx_length"; + case DW_LLE_offset_pair: return "DW_LLE_offset_pair"; + case DW_LLE_default_location: return "DW_LLE_default_location"; + case DW_LLE_base_address: return "DW_LLE_base_address"; + case DW_LLE_start_end: return "DW_LLE_start_end"; + case DW_LLE_start_length: return "DW_LLE_start_length"; + case DW_LLE_GNU_view_pair: return "DW_LLE_GNU_view_pair"; + } + return "DW_LLE_???"; +} + +const HChar* ML_(pp_DW_RLE) ( DW_RLE entry ) +{ + switch (entry) { + case DW_RLE_end_of_list: return "DW_RLE_end_of_list"; + case DW_RLE_base_addressx: return "DW_RLE_base_addressx"; + case DW_RLE_startx_endx: return "DW_RLE_startx_endx"; + case DW_RLE_startx_length: return "DW_RLE_startx_length"; + case DW_RLE_offset_pair: return "DW_RLE_offset_pair"; + case DW_RLE_base_address: return "DW_RLE_base_address"; + case DW_RLE_start_end: return "DW_RLE_start_end"; + case DW_RLE_start_length: return "DW_RLE_start_length"; + } + return "DW_RLE_???"; +} /* ------ To do with evaluation of Dwarf expressions ------ */ diff --git a/coregrind/m_debuginfo/priv_d3basics.h b/coregrind/m_debuginfo/priv_d3basics.h index 9d825e39ef..3f6e5c72c9 100644 --- a/coregrind/m_debuginfo/priv_d3basics.h +++ b/coregrind/m_debuginfo/priv_d3basics.h @@ -768,6 +768,8 @@ const HChar* ML_(pp_DW_children) ( DW_children hashch ); const HChar* ML_(pp_DW_TAG) ( DW_TAG tag ); const HChar* ML_(pp_DW_FORM) ( DW_FORM form ); const HChar* ML_(pp_DW_AT) ( DW_AT attr ); +const HChar* ML_(pp_DW_LLE) ( DW_LLE entry ); +const HChar* ML_(pp_DW_RLE) ( DW_RLE entry ); /* --- To do with evaluation of Dwarf expressions --- */ diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c index 86af340ebc..5a031a6043 100644 --- a/coregrind/m_debuginfo/readdwarf3.c +++ b/coregrind/m_debuginfo/readdwarf3.c @@ -576,6 +576,55 @@ static UWord uncook_die( const CUConst *cc, UWord die, /*OUT*/Bool *type_flag, return die; } +/* Return an entry from .debug_addr with the given index. + Call one of the variants below that do error-checking. */ +static ULong get_debug_addr_entry_common( ULong index, const CUConst* cc ) +{ + vg_assert(cc->cu_has_addr_base); + /* We make the same word-size assumption as DW_FORM_addr. */ + UWord addr_pos = cc->cu_addr_base + index * sizeof(UWord); + Cursor cur; + init_Cursor( &cur, cc->escn_debug_addr, addr_pos, cc->barf, + "get_debug_addr_entry_common: index points outside .debug_addr" ); + return (ULong)(UWord)get_UWord(&cur); +} + +static ULong get_debug_addr_entry_form( ULong index, const CUConst* cc, + DW_FORM form ) +{ + if(!cc->cu_has_addr_base) { + VG_(printf)( + "get_debug_addr_entry_form: %u (%s) without DW_AT_addr_base\n", + form, ML_(pp_DW_FORM)(form)); + cc->barf("get_debug_addr_entry_form: DW_AT_addr_base not set"); + } + return get_debug_addr_entry_common( index, cc ); +} + +static ULong get_debug_addr_entry_lle( ULong index, const CUConst* cc, + DW_LLE entry ) +{ + if(!cc->cu_has_addr_base) { + VG_(printf)( + "get_debug_addr_entry_lle: %u (%s) without DW_AT_addr_base\n", + entry, ML_(pp_DW_LLE)(entry)); + cc->barf("get_debug_addr_entry_lle: DW_AT_addr_base not set"); + } + return get_debug_addr_entry_common( index, cc ); +} + +static ULong get_debug_addr_entry_rle( ULong index, const CUConst* cc, + DW_RLE entry ) +{ + if(!cc->cu_has_addr_base) { + VG_(printf)( + "get_debug_addr_entry_rle: %u (%s) without DW_AT_addr_base\n", + entry, ML_(pp_DW_RLE)(entry)); + cc->barf("get_debug_addr_entry_rle: DW_AT_addr_base not set"); + } + return get_debug_addr_entry_common( index, cc ); +} + /*------------------------------------------------------------*/ /*--- ---*/ /*--- Helper functions for Guarded Expressions ---*/ @@ -784,8 +833,22 @@ static GExpr* make_general_GX ( const CUConst* cc, get_ULEB128( &loc ); break; case DW_LLE_base_addressx: + base = get_debug_addr_entry_lle( get_ULEB128( &loc ), cc, + DW_LLE_base_addressx ); + break; case DW_LLE_startx_endx: + w1 = get_debug_addr_entry_lle( get_ULEB128( &loc ), cc, + DW_LLE_startx_endx ); + w2 = get_debug_addr_entry_lle( get_ULEB128( &loc ), cc, + DW_LLE_startx_endx ); + len = get_ULEB128( &loc ); + break; case DW_LLE_startx_length: + w1 = get_debug_addr_entry_lle( get_ULEB128( &loc ), cc, + DW_LLE_startx_length ); + w2 = w1 + get_ULEB128( &loc ); + len = get_ULEB128( &loc ); + break; case DW_LLE_default_location: default: cc->barf( "Unhandled or unknown loclists entry" ); @@ -1007,8 +1070,20 @@ get_range_list ( const CUConst* cc, w2 = get_UWord ( &ranges ); break; case DW_RLE_base_addressx: + base = get_debug_addr_entry_rle( get_ULEB128( &ranges ), cc, + DW_RLE_base_addressx ); + break; case DW_RLE_startx_endx: + w1 = get_debug_addr_entry_rle( get_ULEB128( &ranges ), cc, + DW_RLE_startx_endx ); + w2 = get_debug_addr_entry_rle( get_ULEB128( &ranges ), cc, + DW_RLE_startx_endx ); + break; case DW_RLE_startx_length: + w1 = get_debug_addr_entry_rle( get_ULEB128( &ranges ), cc, + DW_RLE_startx_length ); + w2 = w1 + get_ULEB128( &ranges ); + break; default: cc->barf( "Unhandled or unknown range list entry" ); done = True; @@ -1312,23 +1387,7 @@ typedef static void get_Form_contents_addr( /*OUT*/FormContents* cts, DW_FORM form, ULong index, const CUConst* cc, Bool td3 ) { - if(!cc->cu_has_addr_base) { - VG_(printf)( - "get_Form_contents_addr: %u (%s) without DW_AT_addr_base\n", - form, ML_(pp_DW_FORM)(form)); - cc->barf("get_Form_contents_addr: DW_AT_addr_base not set"); - } - /* We make the same word-size assumption as DW_FORM_addr. */ - UWord addr_pos = cc->cu_addr_base + index * sizeof(UWord); - Cursor cur; - init_Cursor( &cur, cc->escn_debug_addr, addr_pos, cc->barf, - "get_Form_contents_addr: index points outside .debug_addr" ); - if (TD3) { - HChar* tmp = ML_(cur_read_strdup)(get_DiCursor_from_Cursor(&cur), "di.getFC.1"); - TRACE_D3("(indirect address, offset: 0x%lx): %s", addr_pos, tmp); - ML_(dinfo_free)(tmp); - } - cts->u.val = (ULong)(UWord)get_UWord(&cur); + cts->u.val = get_debug_addr_entry_form( index, cc, form ); cts->szB = sizeof(UWord); TRACE_D3("0x%lx", (UWord)cts->u.val); } |