|  | /* Shared utility routines for GDB to interact with agent. | 
|  |  | 
|  | Copyright (C) 2009-2025 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/>.  */ | 
|  |  | 
|  | #include "target/target.h" | 
|  | #include "gdbsupport/symbol.h" | 
|  | #include <unistd.h> | 
|  | #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 <sys/socket.h> | 
|  | #include <sys/un.h> | 
|  | #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; | 
|  | } |