blob: 235844a7c957b3434c3e1fa89d0da99e91517589 [file]
/* Copyright (C) 2008-2026 Free Software Foundation, Inc.
This file is part of GDB.
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 3 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/>. */
#ifndef GDB_WINDOWS_NAT_H
#define GDB_WINDOWS_NAT_H
#include <atomic>
#include <queue>
#include <vector>
#include "inf-child.h"
#include "nat/windows-nat.h"
#include "ser-event.h"
extern bool debug_exec; /* show execution */
extern bool debug_events; /* show events from kernel */
extern bool debug_memory; /* show target memory accesses */
extern bool debug_exceptions; /* show target exceptions */
#define DEBUG_EXEC(fmt, ...) \
debug_prefixed_printf_cond (debug_exec, "windows exec", fmt, ## __VA_ARGS__)
#define DEBUG_EVENTS(fmt, ...) \
debug_prefixed_printf_cond (debug_events, "windows events", fmt, \
## __VA_ARGS__)
#define DEBUG_MEM(fmt, ...) \
debug_prefixed_printf_cond (debug_memory, "windows mem", fmt, \
## __VA_ARGS__)
#define DEBUG_EXCEPT(fmt, ...) \
debug_prefixed_printf_cond (debug_exceptions, "windows except", fmt, \
## __VA_ARGS__)
using windows_nat::windows_thread_info;
/* A pointer to a function that should return non-zero iff REGNUM
corresponds to one of the segment registers. */
typedef int (segment_register_p_ftype) (int regnum);
/* Maintain a linked list of "so" information. */
struct windows_solib
{
LPVOID load_addr = 0;
CORE_ADDR text_offset = 0;
/* Original name. */
std::string original_name;
/* Expanded form of the name. */
std::string name;
};
/* Flags that can be passed to windows_continue. */
enum windows_continue_flag
{
/* This means we have killed the inferior, so windows_continue
should ignore weird errors due to threads shutting down. */
WCONT_KILLED = 1,
/* This means we expect this windows_continue call to be the last
call to continue the inferior -- we are either mourning it or
detaching. */
WCONT_LAST_CALL = 2,
};
DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
struct windows_per_inferior : public windows_nat::windows_process_info
{
windows_thread_info *find_thread (ptid_t ptid) override;
DWORD handle_output_debug_string (const DEBUG_EVENT &current_event,
struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
void handle_unload_dll (const DEBUG_EVENT &current_event) override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
/* Invalidate the thread context. */
virtual void invalidate_thread_context (windows_thread_info *th) = 0;
int windows_initialization_done = 0;
std::vector<std::unique_ptr<windows_thread_info>> thread_list;
/* Counts of things. */
int saw_create = 0;
int open_process_used = 0;
#ifdef __x86_64__
void *wow64_dbgbreak = nullptr;
#endif
/* This vector maps GDB's idea of a register's number into an offset
in the windows exception context vector.
It also contains the bit mask needed to load the register in question.
The contents of this table can only be computed by the units
that provide CPU-specific support for Windows native debugging.
One day we could read a reg, we could inspect the context we
already have loaded, if it doesn't have the bit set that we need,
we read that set of registers in using GetThreadContext. If the
context already contains what we need, we just unpack it. Then to
write a register, first we have to ensure that the context contains
the other regs of the group, and then we copy the info in and set
out bit. */
const int *mappings = nullptr;
std::vector<windows_solib> solibs;
#ifdef __CYGWIN__
/* The starting and ending address of the cygwin1.dll text segment. */
CORE_ADDR cygwin_load_start = 0;
CORE_ADDR cygwin_load_end = 0;
#endif /* __CYGWIN__ */
};
struct windows_nat_target : public inf_child_target
{
windows_nat_target ();
void close () override;
thread_control_capabilities get_thread_control_capabilities () override
{ return tc_schedlock; }
void attach (const char *, int) override;
bool attach_no_wait () override
{ return true; }
void detach (inferior *, int) override;
void resume (ptid_t, int , enum gdb_signal) override;
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
void fetch_registers (struct regcache *, int) override;
void store_registers (struct regcache *, int) override;
bool stopped_by_sw_breakpoint () override;
bool supports_stopped_by_sw_breakpoint () override
{
return true;
}
enum target_xfer_status xfer_partial (enum target_object object,
const char *annex,
gdb_byte *readbuf,
const gdb_byte *writebuf,
ULONGEST offset, ULONGEST len,
ULONGEST *xfered_len) override;
void files_info () override;
void kill () override;
void create_inferior (const char *, const std::string &,
char **, int) override;
void mourn_inferior () override;
bool thread_alive (ptid_t ptid) override;
std::string pid_to_str (ptid_t) override;
void interrupt () override;
void pass_ctrlc () override;
void stop (ptid_t ptid) override;
const char *pid_to_exec_file (int pid) override;
ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override;
bool get_tib_address (ptid_t ptid, CORE_ADDR *addr) override;
const char *thread_name (struct thread_info *) override;
ptid_t get_windows_debug_event (int pid, struct target_waitstatus *ourstatus,
target_wait_flags options,
DEBUG_EVENT *current_event);
void do_initial_windows_stuff (DWORD pid, bool attaching);
bool supports_disable_randomization () override
{
return windows_nat::disable_randomization_available ();
}
bool can_async_p () override
{
return true;
}
bool is_async_p () override
{
return m_is_async;
}
void async (bool enable) override;
int async_wait_fd () override
{
return serial_event_fd (m_wait_event);
}
protected:
/* Initialize arch-specific data for a new inferior (debug registers,
register mappings). If ATTACHING is true, we're attaching to an
already-running process. */
virtual void initialize_windows_arch (bool attaching) = 0;
/* Cleanup arch-specific data after inferior exit. */
virtual void cleanup_windows_arch () = 0;
/* Prepare the thread context for continuing. */
virtual void thread_context_continue (windows_thread_info *th,
int killed) = 0;
/* Set the stepping bit in the thread context. */
virtual void thread_context_step (windows_thread_info *th) = 0;
/* Fetches register number R from the given windows_thread_info,
and supplies its value to the given regcache.
This function assumes that R is non-negative. A failed assertion
is raised if that is not true.
This function assumes that TH->RELOAD_CONTEXT is not set, meaning
that the windows_thread_info has an up-to-date context. A failed
assertion is raised if that assumption is violated. */
virtual void fetch_one_register (struct regcache *regcache,
windows_thread_info *th, int r) = 0;
/* Collect the register number R from the given regcache, and store
its value into the corresponding area of the given thread's context.
This function assumes that R is non-negative. A failed assertion
assertion is raised if that is not true. */
virtual void store_one_register (const struct regcache *regcache,
windows_thread_info *th, int r) = 0;
/* Determine if ER contains a software-breakpoint. */
virtual bool is_sw_breakpoint (const EXCEPTION_RECORD *er) const = 0;
private:
windows_thread_info *add_thread (ptid_t ptid, HANDLE h, void *tlb,
bool main_thread_p);
void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p);
DWORD fake_create_process (const DEBUG_EVENT &current_event);
void continue_one_thread (windows_thread_info *th,
windows_continue_flags cont_flags);
BOOL windows_continue (DWORD continue_status, int id,
windows_continue_flags cont_flags = 0);
/* Helper function to start process_thread. */
static DWORD WINAPI process_thread_starter (LPVOID self);
/* This function implements the background thread that starts
inferiors and waits for events. */
void process_thread ();
/* Push FUNC onto the queue of requests for process_thread, and wait
until it has been called. On Windows, certain debugging
functions can only be called by the thread that started (or
attached to) the inferior. These are all done in the worker
thread, via calls to this method. If FUNC returns true,
process_thread will wait for debug events when FUNC returns. */
void do_synchronously (gdb::function_view<bool ()> func);
/* This waits for a debug event, dispatching to the worker thread as
needed. */
void wait_for_debug_event_main_thread (DEBUG_EVENT *event);
/* This continues the last debug event, dispatching to the worker
thread as needed. */
void continue_last_debug_event_main_thread (const char *context_str,
DWORD continue_status,
bool last_call = false);
/* Force the process_thread thread to return from WaitForDebugEvent.
PROCESS_ALIVE is set to false if the inferior process exits while
we're trying to break out the process_thread thread. This can
happen because this is called while all threads are running free,
while we're trying to detach. */
void break_out_process_thread (bool &process_alive);
/* Queue used to send requests to process_thread. This is
implicitly locked. */
std::queue<gdb::function_view<bool ()>> m_queue;
/* Event used to signal process_thread that an item has been
pushed. */
HANDLE m_pushed_event;
/* Event used by process_thread to indicate that it has processed a
single function call. */
HANDLE m_response_event;
/* Serial event used to communicate wait event availability to the
main loop. */
serial_event *m_wait_event;
/* The last debug event, when M_WAIT_EVENT has been set. */
DEBUG_EVENT m_last_debug_event {};
/* True if a debug event is pending. */
std::atomic<bool> m_debug_event_pending { false };
/* True if currently in async mode. */
bool m_is_async = false;
/* True if we last called ContinueDebugEvent and the process_thread
thread is now waiting for events. False if WaitForDebugEvent
already returned an event, and we need to ContinueDebugEvent
again to restart the inferior. */
bool m_continued = false;
};
/* Check if Windows API call succeeds, and otherwise print error code
and description. */
void check (BOOL ok, const char *file, int line);
#define CHECK(x) check (x, __FILE__,__LINE__)
/* The current process. */
extern windows_per_inferior *windows_process;
/* segment_register_p_ftype implementation for x86. */
int i386_windows_segment_register_p (int regnum);
/* context register offsets for x86. */
extern const int i386_mappings[];
#ifdef __x86_64__
/* segment_register_p_ftype implementation for amd64. */
int amd64_windows_segment_register_p (int regnum);
/* context register offsets for amd64. */
extern const int amd64_mappings[];
#endif
#endif /* GDB_WINDOWS_NAT_H */