| /* Shared utility routines for GDB to interact with agent. | 
 |  | 
 |    Copyright (C) 2009-2020 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 "common-defs.h" | 
 | #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.  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, const char *cmd, int len) | 
 | { | 
 |   int fd; | 
 |   int tid = agent_get_helper_thread_id (); | 
 |   ptid_t ptid = ptid_t (pid, tid, 0); | 
 |  | 
 |   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; | 
 | } |