mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
The valgrind gdbserver inheritated a register cache from the original GDBserver implementation. The objective of this register cache was to improve the performance of GDB-> gdbserver -> inferior by avoiding the gdbserver having to do ptrace system calls each time GDB wants to read or write a register when the inferior is stopped. This register cache is however not useful for the valgrind gdbserver: As the valgrind gdbserver being co-located with the inferior, it can directly and efficiently read and write registers from/to the VEX state. This commit ensures the valgrind GDBserver directly reads from VEX state instead of fetching the registers from the VEX state and copying them to the gdbserver regcache. Similarly, when GDB wants to modify a register, the valgrind GDB server now directly writes into the VEX state instead of writing the registers in the regcache and having the regcache flushed to the VEX state when execution is resumed. The files regcache.h and regcache.c are still useful as they provide a translation between a register number, a register name on one side and the offset in an array of bytes in the format expected by GDB. The regcache now is only used to create this array of bytes, which is itself only used temporarily when GDB reads or writes the complete set of registers instead of reading/writing one register at a time. Removing the usage of this regcache avoids the bug 458915. The regcache was causing the bug in the following circumstances: We have a thread executing code, while we have a bunch of threads that are blocked in a syscall. When a thread is blocked in a syscall, the VEX rax register is set to the syscall nr. A thread executing code will check from time to time if GDB tries to attach. When GDB attaches to the valgrind gdbserver , the thread executing code will copy the registers from all the threads to the thread gdbserver regcache. However, the threads blocked in a system call can be unblocked e.g. because the epoll_wait timeout expires. In such a case, the thread will still execute the few instructions that follow the syscall instructions till the thread is blocked trying to acquire the scheduler lock. These instructions are extracting the syscall return code from the host register and copies it to the valgrind VEX state. However, this assembly code is not aware that there is a gdbserver cache. When the unblocked thread is on the acquire lock statement, the GDB server regcache is now inconsistent (i.e. different from) the real VEX state. When finally GDB tells GDB server to continue execution, the GDB server wrongly detected that its regcache was modified compared to the VEX state: the regcache still contains e.g. for the rax register the syscall number while the unblocked thread has put the syscall return code in the VEX rax register. GDBserver then flushed the regcache rax (containing the syscall number) to the VEX rax. And that led to the detected bug that the syscall return code seen by the guest application was the syscall number. Removing the regcache ensures that GDB directly reads the values from VEX and directly writes to VEX state. Note that we could still have GDB reading from VEX a register value that will be changed a few instructions later. GDB will then show some (slightly) old/obsolete values for some registers to the user. This should have no consequence as long as GDB does not try to modify the registers to execute an inferior call. The bug did not happen systematically as most of the time, when threads are blocked in syscalls, vgdb attaches using ptrace to the valgrind process. When vgdb attaches with ptrace, it stops all the threads using linux syscall. When vgdb stops the threads, the threads blocked in a syscall will not execute the instructions between the syscall instruction and the lock acquire, and so the problem of desynchronisation between the VEX state and the register cache could not happen. This commit touches architecture specific files of the gdbserver, it has been tested on amd64/debian, on pcc64/centos and on arm64/ubuntu. Possibly, some untested arch might not compile but the fix should be trivial.
85 lines
2.8 KiB
C
85 lines
2.8 KiB
C
/* Register support routines for the remote server for GDB.
|
|
Copyright (C) 2001, 2002, 2012 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
It has been modified to integrate it in valgrind
|
|
|
|
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, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA. */
|
|
|
|
#ifndef REGCACHE_H
|
|
#define REGCACHE_H
|
|
|
|
/* Defines support routines to get/set registers for the valgrind
|
|
remote GDB server.
|
|
This file used to provide a real register cache, where the register
|
|
values were written to by GDB without directly reaching the valgrind VEX
|
|
state. In the real GDB gdbserver, this cache was used to avoid a ptrace
|
|
system call each time a register has to be re-read. In valgrind, registers
|
|
are directly accessible by the embedded gdbserver. So, read/write registers
|
|
operations by GDB are directly executed from/to the valgrind VEX registers. */
|
|
|
|
|
|
struct inferior_list_entry;
|
|
|
|
/* Create a new register cache for INFERIOR. */
|
|
|
|
void *new_register_cache (void);
|
|
|
|
/* Release all memory associated with the register cache for INFERIOR. */
|
|
|
|
void free_register_cache (void *regcache);
|
|
|
|
/* Convert all registers to a string in the currently specified remote
|
|
format. */
|
|
|
|
void registers_to_string (char *buf);
|
|
|
|
/* Convert a string to register values and fill our register cache. */
|
|
|
|
void registers_from_string (const char *buf);
|
|
|
|
/* Return the size in bytes of a string-encoded register packet. */
|
|
|
|
int registers_length (void);
|
|
|
|
/* Return a pointer to the description of register ``n''. */
|
|
|
|
struct reg *find_register_by_number (int n);
|
|
|
|
int register_size (int n);
|
|
|
|
int find_regno (const char *name);
|
|
|
|
extern const char **gdbserver_expedite_regs;
|
|
|
|
/* Sets the value of register N to buf content. */
|
|
void supply_register (int n, const void *buf);
|
|
|
|
/* Reads register data from buf (hex string in target byte order)
|
|
and stores it in the register cache. */
|
|
void supply_register_from_string (int n, const char *buf);
|
|
|
|
/* Sets the value of register identified by NAME to buf content. */
|
|
void supply_register_by_name (const char *name, const void *buf);
|
|
|
|
void collect_register (int n, void *buf);
|
|
|
|
void collect_register_as_string (int n, char *buf);
|
|
|
|
void collect_register_by_name (const char *name, void *buf);
|
|
|
|
#endif /* REGCACHE_H */
|