| /* 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; | |
| } |