/* Shared utility routines for GDB to interact with agent. Copyright (C) 2009-2023 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 . */ #include "common-defs.h" #include "target/target.h" #include "gdbsupport/symbol.h" #include #include "filestuff.h" #define IPA_SYM_STRUCT_NAME ipa_sym_addresses_common #include "agent.h" bool debug_agent = false; /* A stdarg wrapper for debug_vprintf. */ static void ATTRIBUTE_PRINTF (1, 2) debug_agent_printf (const char *fmt, ...) { va_list ap; if (!debug_agent) return; va_start (ap, fmt); debug_vprintf (fmt, ap); va_end (ap); } #define DEBUG_AGENT debug_agent_printf /* Global flag to determine using agent or not. */ bool use_agent = false; /* Addresses of in-process agent's symbols both GDB and GDBserver cares about. */ struct ipa_sym_addresses_common { CORE_ADDR addr_helper_thread_id; CORE_ADDR addr_cmd_buf; CORE_ADDR addr_capability; }; /* Cache of the helper thread id. FIXME: this global should be made per-process. */ static uint32_t helper_thread_id = 0; static struct { const char *name; int offset; } symbol_list[] = { IPA_SYM(helper_thread_id), IPA_SYM(cmd_buf), IPA_SYM(capability), }; static struct ipa_sym_addresses_common ipa_sym_addrs; static bool all_agent_symbols_looked_up = false; bool agent_loaded_p (void) { return all_agent_symbols_looked_up; } /* Look up all symbols needed by agent. Return 0 if all the symbols are found, return non-zero otherwise. */ int agent_look_up_symbols (void *arg) { all_agent_symbols_looked_up = false; for (int i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++) { CORE_ADDR *addrp = (CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset); struct objfile *objfile = (struct objfile *) arg; if (find_minimal_symbol_address (symbol_list[i].name, addrp, objfile) != 0) { DEBUG_AGENT ("symbol `%s' not found\n", symbol_list[i].name); return -1; } } all_agent_symbols_looked_up = true; return 0; } static unsigned int agent_get_helper_thread_id (void) { if (helper_thread_id == 0) { if (target_read_uint32 (ipa_sym_addrs.addr_helper_thread_id, &helper_thread_id)) warning (_("Error reading helper thread's id in lib")); } return helper_thread_id; } #ifdef HAVE_SYS_UN_H #include #include #define SOCK_DIR P_tmpdir #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path) #endif #endif /* Connects to synchronization socket. PID is the pid of inferior, which is used to set up the connection socket. */ static int gdb_connect_sync_socket (int pid) { #ifdef HAVE_SYS_UN_H struct sockaddr_un addr = {}; int res, fd; char path[UNIX_PATH_MAX]; res = xsnprintf (path, UNIX_PATH_MAX, "%s/gdb_ust%d", P_tmpdir, pid); if (res >= UNIX_PATH_MAX) return -1; res = fd = gdb_socket_cloexec (PF_UNIX, SOCK_STREAM, 0); if (res == -1) { warning (_("error opening sync socket: %s"), safe_strerror (errno)); return -1; } addr.sun_family = AF_UNIX; res = xsnprintf (addr.sun_path, UNIX_PATH_MAX, "%s", path); if (res >= UNIX_PATH_MAX) { warning (_("string overflow allocating socket name")); close (fd); return -1; } res = connect (fd, (struct sockaddr *) &addr, sizeof (addr)); if (res == -1) { warning (_("error connecting sync socket (%s): %s. " "Make sure the directory exists and that it is writable."), path, safe_strerror (errno)); close (fd); return -1; } return fd; #else return -1; #endif } /* Execute an agent command in the inferior. PID is the value of pid of the inferior. CMD is the buffer for command. It is assumed to be at least IPA_CMD_BUF_SIZE bytes long. GDB or GDBserver will store the command into it and fetch the return result from CMD. The interaction between GDB/GDBserver and the agent is synchronized by a synchronization socket. Return zero if success, otherwise return non-zero. */ int agent_run_command (int pid, char *cmd, int len) { int fd; int tid = agent_get_helper_thread_id (); ptid_t ptid = ptid_t (pid, tid); int ret = target_write_memory (ipa_sym_addrs.addr_cmd_buf, (gdb_byte *) cmd, len); if (ret != 0) { warning (_("unable to write")); return -1; } DEBUG_AGENT ("agent: resumed helper thread\n"); /* Resume helper thread. */ target_continue_no_signal (ptid); fd = gdb_connect_sync_socket (pid); if (fd >= 0) { char buf[1] = ""; DEBUG_AGENT ("agent: signalling helper thread\n"); do { ret = write (fd, buf, 1); } while (ret == -1 && errno == EINTR); DEBUG_AGENT ("agent: waiting for helper thread's response\n"); do { ret = read (fd, buf, 1); } while (ret == -1 && errno == EINTR); close (fd); DEBUG_AGENT ("agent: helper thread's response received\n"); } else return -1; /* Need to read response with the inferior stopped. */ if (ptid != null_ptid) { /* Stop thread PTID. */ DEBUG_AGENT ("agent: stop helper thread\n"); target_stop_and_wait (ptid); } if (fd >= 0) { if (target_read_memory (ipa_sym_addrs.addr_cmd_buf, (gdb_byte *) cmd, IPA_CMD_BUF_SIZE)) { warning (_("Error reading command response")); return -1; } } return 0; } /* Each bit of it stands for a capability of agent. */ static uint32_t agent_capability = 0; /* Return true if agent has capability AGENT_CAP, otherwise return false. */ bool agent_capability_check (enum agent_capa agent_capa) { if (agent_capability == 0) { if (target_read_uint32 (ipa_sym_addrs.addr_capability, &agent_capability)) warning (_("Error reading capability of agent")); } return (agent_capability & agent_capa) != 0; } /* Invalidate the cache of agent capability, so we'll read it from inferior again. Call it when launches a new program or reconnect to remote stub. */ void agent_capability_invalidate (void) { agent_capability = 0; }