| Index: gdb/common/agent.c
|
| diff --git a/gdb/common/agent.c b/gdb/common/agent.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..43f1b65e78145ac4a6c42257d5eab24c8d5d0152
|
| --- /dev/null
|
| +++ b/gdb/common/agent.c
|
| @@ -0,0 +1,356 @@
|
| +/* Shared utility routines for GDB to interact with agent.
|
| +
|
| + Copyright (C) 2009-2012 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/>. */
|
| +
|
| +#ifdef GDBSERVER
|
| +#include "server.h"
|
| +#else
|
| +#include "defs.h"
|
| +#include "target.h"
|
| +#include "inferior.h" /* for non_stop */
|
| +#endif
|
| +
|
| +#include <string.h>
|
| +#include <unistd.h>
|
| +#include "agent.h"
|
| +
|
| +int debug_agent = 0;
|
| +
|
| +#ifdef GDBSERVER
|
| +#define DEBUG_AGENT(fmt, args...) \
|
| + if (debug_agent) \
|
| + fprintf (stderr, fmt, ##args);
|
| +#else
|
| +#define DEBUG_AGENT(fmt, args...) \
|
| + if (debug_agent) \
|
| + fprintf_unfiltered (gdb_stdlog, fmt, ##args);
|
| +#endif
|
| +
|
| +/* Global flag to determine using agent or not. */
|
| +int use_agent = 0;
|
| +
|
| +/* Addresses of in-process agent's symbols both GDB and GDBserver cares
|
| + about. */
|
| +
|
| +struct ipa_sym_addresses
|
| +{
|
| + 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 unsigned int helper_thread_id = 0;
|
| +
|
| +static struct
|
| +{
|
| + const char *name;
|
| + int offset;
|
| + int required;
|
| +} symbol_list[] = {
|
| + IPA_SYM(helper_thread_id),
|
| + IPA_SYM(cmd_buf),
|
| + IPA_SYM(capability),
|
| +};
|
| +
|
| +static struct ipa_sym_addresses ipa_sym_addrs;
|
| +
|
| +static int all_agent_symbols_looked_up = 0;
|
| +
|
| +int
|
| +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)
|
| +{
|
| + int i;
|
| +
|
| + all_agent_symbols_looked_up = 0;
|
| +
|
| + for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
|
| + {
|
| + CORE_ADDR *addrp =
|
| + (CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset);
|
| +#ifdef GDBSERVER
|
| +
|
| + if (look_up_one_symbol (symbol_list[i].name, addrp, 1) == 0)
|
| +#else
|
| + struct minimal_symbol *sym =
|
| + lookup_minimal_symbol (symbol_list[i].name, NULL,
|
| + (struct objfile *) arg);
|
| +
|
| + if (sym != NULL)
|
| + *addrp = SYMBOL_VALUE_ADDRESS (sym);
|
| + else
|
| +#endif
|
| + {
|
| + DEBUG_AGENT ("symbol `%s' not found\n", symbol_list[i].name);
|
| + return -1;
|
| + }
|
| + }
|
| +
|
| + all_agent_symbols_looked_up = 1;
|
| + return 0;
|
| +}
|
| +
|
| +static unsigned int
|
| +agent_get_helper_thread_id (void)
|
| +{
|
| + if (helper_thread_id == 0)
|
| + {
|
| +#ifdef GDBSERVER
|
| + if (read_inferior_memory (ipa_sym_addrs.addr_helper_thread_id,
|
| + (unsigned char *) &helper_thread_id,
|
| + sizeof helper_thread_id))
|
| +#else
|
| + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
|
| + gdb_byte buf[4];
|
| +
|
| + if (target_read_memory (ipa_sym_addrs.addr_helper_thread_id,
|
| + buf, sizeof buf) == 0)
|
| + helper_thread_id = extract_unsigned_integer (buf, sizeof buf,
|
| + byte_order);
|
| + else
|
| +#endif
|
| + {
|
| + 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 = socket (PF_UNIX, SOCK_STREAM, 0);
|
| + if (res == -1)
|
| + {
|
| + warning (_("error opening sync socket: %s"), 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, 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_build (pid, tid, 0);
|
| +
|
| +#ifdef GDBSERVER
|
| + int ret = write_inferior_memory (ipa_sym_addrs.addr_cmd_buf,
|
| + (const unsigned char *) cmd, len);
|
| +#else
|
| + int ret = target_write_memory (ipa_sym_addrs.addr_cmd_buf, cmd, len);
|
| +#endif
|
| +
|
| + if (ret != 0)
|
| + {
|
| + warning (_("unable to write"));
|
| + return -1;
|
| + }
|
| +
|
| + DEBUG_AGENT ("agent: resumed helper thread\n");
|
| +
|
| + /* Resume helper thread. */
|
| +#ifdef GDBSERVER
|
| +{
|
| + struct thread_resume resume_info;
|
| +
|
| + resume_info.thread = ptid;
|
| + resume_info.kind = resume_continue;
|
| + resume_info.sig = GDB_SIGNAL_0;
|
| + (*the_target->resume) (&resume_info, 1);
|
| +}
|
| +#else
|
| + target_resume (ptid, 0, GDB_SIGNAL_0);
|
| +#endif
|
| +
|
| + fd = gdb_connect_sync_socket (pid);
|
| + if (fd >= 0)
|
| + {
|
| + char buf[1] = "";
|
| + int ret;
|
| +
|
| + 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_equal (ptid, null_ptid))
|
| + {
|
| + struct target_waitstatus status;
|
| + int was_non_stop = non_stop;
|
| + /* Stop thread PTID. */
|
| + DEBUG_AGENT ("agent: stop helper thread\n");
|
| +#ifdef GDBSERVER
|
| + {
|
| + struct thread_resume resume_info;
|
| +
|
| + resume_info.thread = ptid;
|
| + resume_info.kind = resume_stop;
|
| + resume_info.sig = GDB_SIGNAL_0;
|
| + (*the_target->resume) (&resume_info, 1);
|
| + }
|
| +
|
| + non_stop = 1;
|
| + mywait (ptid, &status, 0, 0);
|
| +#else
|
| + non_stop = 1;
|
| + target_stop (ptid);
|
| +
|
| + memset (&status, 0, sizeof (status));
|
| + target_wait (ptid, &status, 0);
|
| +#endif
|
| + non_stop = was_non_stop;
|
| + }
|
| +
|
| + if (fd >= 0)
|
| + {
|
| +#ifdef GDBSERVER
|
| + if (read_inferior_memory (ipa_sym_addrs.addr_cmd_buf,
|
| + (unsigned char *) cmd, IPA_CMD_BUF_SIZE))
|
| +#else
|
| + if (target_read_memory (ipa_sym_addrs.addr_cmd_buf, (gdb_byte *) cmd,
|
| + IPA_CMD_BUF_SIZE))
|
| +#endif
|
| + {
|
| + warning (_("Error reading command response"));
|
| + return -1;
|
| + }
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +/* Each bit of it stands for a capability of agent. */
|
| +static unsigned int agent_capability = 0;
|
| +
|
| +/* Return true if agent has capability AGENT_CAP, otherwise return false. */
|
| +
|
| +int
|
| +agent_capability_check (enum agent_capa agent_capa)
|
| +{
|
| + if (agent_capability == 0)
|
| + {
|
| +#ifdef GDBSERVER
|
| + if (read_inferior_memory (ipa_sym_addrs.addr_capability,
|
| + (unsigned char *) &agent_capability,
|
| + sizeof agent_capability))
|
| +#else
|
| + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
|
| + gdb_byte buf[4];
|
| +
|
| + if (target_read_memory (ipa_sym_addrs.addr_capability,
|
| + buf, sizeof buf) == 0)
|
| + agent_capability = extract_unsigned_integer (buf, sizeof buf,
|
| + byte_order);
|
| + else
|
| +#endif
|
| + warning (_("Error reading capability of agent"));
|
| + }
|
| + return agent_capability & agent_capa;
|
| +}
|
| +
|
| +/* 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;
|
| +}
|
|
|