diff options
Diffstat (limited to 'gdb/gdbsupport')
109 files changed, 15376 insertions, 0 deletions
diff --git a/gdb/gdbsupport/agent.c b/gdb/gdbsupport/agent.c new file mode 100644 index 00000000000..02cad463699 --- /dev/null +++ b/gdb/gdbsupport/agent.c @@ -0,0 +1,282 @@ +/* Shared utility routines for GDB to interact with agent. + + Copyright (C) 2009-2019 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" + +int debug_agent = 0; + +/* 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. */ +int use_agent = 0; + +/* 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 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); + 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 = 1; + 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"), 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_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. */ + +int +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; +} + +/* 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; +} diff --git a/gdb/gdbsupport/agent.h b/gdb/gdbsupport/agent.h new file mode 100644 index 00000000000..4f054987dd0 --- /dev/null +++ b/gdb/gdbsupport/agent.h @@ -0,0 +1,68 @@ +/* Shared utility routines for GDB to interact with agent. + + Copyright (C) 2009-2019 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/>. */ + +#ifndef COMMON_AGENT_H +#define COMMON_AGENT_H + +#include "gdbsupport/preprocessor.h" + +int agent_run_command (int pid, const char *cmd, int len); + +int agent_look_up_symbols (void *); + +#define IPA_SYM_EXPORTED_NAME(SYM) gdb_agent_ ## SYM + +/* Define an entry in an IPA symbol list array. If IPA_SYM is used, the macro + IPA_SYM_STRUCT_NAME must be defined to the structure name holding the IPA + symbol addresses in that particular file, before including + gdbsupport/agent.h. */ +#define IPA_SYM(SYM) \ + { \ + STRINGIFY (IPA_SYM_EXPORTED_NAME (SYM)), \ + offsetof (IPA_SYM_STRUCT_NAME, addr_ ## SYM) \ + } + +/* The size in bytes of the buffer used to talk to the IPA helper + thread. */ +#define IPA_CMD_BUF_SIZE 1024 + +int agent_loaded_p (void); + +extern int debug_agent; + +extern int use_agent; + +/* Capability of agent. Different agents may have different capabilities, + such as installing fast tracepoint or evaluating breakpoint conditions. + Capabilities are represented by bit-maps, and each capability occupies one + bit. */ + +enum agent_capa +{ + /* Capability to install fast tracepoint. */ + AGENT_CAPA_FAST_TRACE = 0x1, + /* Capability to install static tracepoint. */ + AGENT_CAPA_STATIC_TRACE = (0x1 << 1), +}; + +int agent_capability_check (enum agent_capa); + +void agent_capability_invalidate (void); + +#endif /* COMMON_AGENT_H */ diff --git a/gdb/gdbsupport/array-view.h b/gdb/gdbsupport/array-view.h new file mode 100644 index 00000000000..ad7933bc1fb --- /dev/null +++ b/gdb/gdbsupport/array-view.h @@ -0,0 +1,259 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_ARRAY_VIEW_H +#define COMMON_ARRAY_VIEW_H + +#include "traits.h" +#include <type_traits> + +/* An array_view is an abstraction that provides a non-owning view + over a sequence of contiguous objects. + + A way to put it is that array_view is to std::vector (and + std::array and built-in arrays with rank==1) like std::string_view + is to std::string. + + The main intent of array_view is to use it as function input + parameter type, making it possible to pass in any sequence of + contiguous objects, irrespective of whether the objects live on the + stack or heap and what actual container owns them. Implicit + construction from the element type is supported too, making it easy + to call functions that expect an array of elements when you only + have one element (usually on the stack). For example: + + struct A { .... }; + void function (gdb::array_view<A> as); + + std::vector<A> std_vec = ...; + std::array<A, N> std_array = ...; + A array[] = {...}; + A elem; + + function (std_vec); + function (std_array); + function (array); + function (elem); + + Views can be either mutable or const. A const view is simply + created by specifying a const T as array_view template parameter, + in which case operator[] of non-const array_view objects ends up + returning const references. Making the array_view itself const is + analogous to making a pointer itself be const. I.e., disables + re-seating the view/pointer. + + Since array_view objects are small (pointer plus size), and + designed to be trivially copyable, they should generally be passed + around by value. + + You can find unit tests covering the whole API in + unittests/array-view-selftests.c. */ + +namespace gdb { + +template <typename T> +class array_view +{ + /* True iff decayed T is the same as decayed U. E.g., we want to + say that 'T&' is the same as 'const T'. */ + template <typename U> + using IsDecayedT = typename std::is_same<typename std::decay<T>::type, + typename std::decay<U>::type>; + + /* True iff decayed T is the same as decayed U, and 'U *' is + implicitly convertible to 'T *'. This is a requirement for + several methods. */ + template <typename U> + using DecayedConvertible = gdb::And<IsDecayedT<U>, + std::is_convertible<U *, T *>>; + +public: + using value_type = T; + using reference = T &; + using const_reference = const T &; + using size_type = size_t; + + /* Default construction creates an empty view. */ + constexpr array_view () noexcept + : m_array (nullptr), m_size (0) + {} + + /* Create an array view over a single object of the type of an + array_view element. The created view as size==1. This is + templated on U to allow constructing a array_view<const T> over a + (non-const) T. The "convertible" requirement makes sure that you + can't create an array_view<T> over a const T. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U &elem) noexcept + : m_array (&elem), m_size (1) + {} + + /* Same as above, for rvalue references. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U &&elem) noexcept + : m_array (&elem), m_size (1) + {} + + /* Create an array view from a pointer to an array and an element + count. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U *array, size_t size) noexcept + : m_array (array), m_size (size) + {} + + /* Create an array view from a range. This is templated on both U + an V to allow passing in a mix of 'const T *' and 'T *'. */ + template<typename U, typename V, + typename = Requires<DecayedConvertible<U>>, + typename = Requires<DecayedConvertible<V>>> + constexpr array_view (U *begin, V *end) noexcept + : m_array (begin), m_size (end - begin) + {} + + /* Create an array view from an array. */ + template<typename U, size_t Size, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U (&array)[Size]) noexcept + : m_array (array), m_size (Size) + {} + + /* Create an array view from a contiguous container. E.g., + std::vector and std::array. */ + template<typename Container, + typename = Requires<gdb::Not<IsDecayedT<Container>>>, + typename + = Requires<std::is_convertible + <decltype (std::declval<Container> ().data ()), + T *>>, + typename + = Requires<std::is_convertible + <decltype (std::declval<Container> ().size ()), + size_type>>> + constexpr array_view (Container &&c) noexcept + : m_array (c.data ()), m_size (c.size ()) + {} + + /* Observer methods. Some of these can't be constexpr until we + require C++14. */ + /*constexpr14*/ T *data () noexcept { return m_array; } + constexpr const T *data () const noexcept { return m_array; } + + /*constexpr14*/ T *begin () noexcept { return m_array; } + constexpr const T *begin () const noexcept { return m_array; } + + /*constexpr14*/ T *end () noexcept { return m_array + m_size; } + constexpr const T *end () const noexcept { return m_array + m_size; } + + /*constexpr14*/ reference operator[] (size_t index) noexcept + { return m_array[index]; } + constexpr const_reference operator[] (size_t index) const noexcept + { return m_array[index]; } + + constexpr size_type size () const noexcept { return m_size; } + constexpr bool empty () const noexcept { return m_size == 0; } + + /* Slice an array view. */ + + /* Return a new array view over SIZE elements starting at START. */ + constexpr array_view<T> slice (size_type start, size_type size) const noexcept + { return {m_array + start, size}; } + + /* Return a new array view over all the elements after START, + inclusive. */ + constexpr array_view<T> slice (size_type start) const noexcept + { return {m_array + start, size () - start}; } + +private: + T *m_array; + size_type m_size; +}; + +/* Compare LHS and RHS for (deep) equality. That is, whether LHS and + RHS have the same sizes, and whether each pair of elements of LHS + and RHS at the same position compares equal. */ + +template <typename T> +bool +operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) +{ + if (lhs.size () != rhs.size ()) + return false; + + for (size_t i = 0; i < lhs.size (); i++) + if (!(lhs[i] == rhs[i])) + return false; + + return true; +} + +/* Compare two array_views for inequality. */ + +template <typename T> +bool +operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) +{ + return !(lhs == rhs); +} + +/* Create an array view from a pointer to an array and an element + count. + + This is useful as alternative to constructing an array_view using + brace initialization when the size variable you have handy is of + signed type, since otherwise without an explicit cast the code + would be ill-formed. + + For example, with: + + extern void foo (int, int, gdb::array_view<value *>); + + value *args[2]; + int nargs; + foo (1, 2, {values, nargs}); + + You'd get: + + source.c:10: error: narrowing conversion of ‘nargs’ from ‘int’ to + ‘size_t {aka long unsigned int}’ inside { } [-Werror=narrowing] + + You could fix it by writing the somewhat distracting explicit cast: + + foo (1, 2, {values, (size_t) nargs}); + + Or by instantiating an array_view explicitly: + + foo (1, 2, gdb::array_view<value *>(values, nargs)); + + Or, better, using make_array_view, which has the advantage of + inferring the arrav_view element's type: + + foo (1, 2, gdb::make_array_view (values, nargs)); +*/ + +template<typename U> +constexpr inline array_view<U> +make_array_view (U *array, size_t size) noexcept +{ + return {array, size}; +} + +} /* namespace gdb */ + +#endif diff --git a/gdb/gdbsupport/ax.def b/gdb/gdbsupport/ax.def new file mode 100644 index 00000000000..7b4c5ed7cca --- /dev/null +++ b/gdb/gdbsupport/ax.def @@ -0,0 +1,97 @@ +/* Definition of agent opcode values. -*- c -*- + Copyright (C) 1998-2019 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/>. */ + +/* The actual values of the various bytecode operations. + + Other independent implementations of the agent bytecode engine will + rely on the exact values of these enums, and may not be recompiled + when we change this table. The numeric values should remain fixed + whenever possible. Thus, we assign them values explicitly here (to + allow gaps to form safely), and the disassembly table in + agentexpr.h behaves like an opcode map. If you want to see them + grouped logically, see doc/agentexpr.texi. + + Each line is of the form: + + DEFOP (name, size, data_size, consumed, produced, opcode) + + NAME is the name of the operation. + SIZE is the number of argument bytes that the operation takes from + the bytecode stream. + DATA_SIZE is the size of data operated on, in bits, for operations + that care (ref and const). It is zero otherwise. + CONSUMED is the number of stack elements consumed. + PRODUCED is the number of stack elements produced. + OPCODE is the operation's encoding. */ + +DEFOP (float, 0, 0, 0, 0, 0x01) +DEFOP (add, 0, 0, 2, 1, 0x02) +DEFOP (sub, 0, 0, 2, 1, 0x03) +DEFOP (mul, 0, 0, 2, 1, 0x04) +DEFOP (div_signed, 0, 0, 2, 1, 0x05) +DEFOP (div_unsigned, 0, 0, 2, 1, 0x06) +DEFOP (rem_signed, 0, 0, 2, 1, 0x07) +DEFOP (rem_unsigned, 0, 0, 2, 1, 0x08) +DEFOP (lsh, 0, 0, 2, 1, 0x09) +DEFOP (rsh_signed, 0, 0, 2, 1, 0x0a) +DEFOP (rsh_unsigned, 0, 0, 2, 1, 0x0b) +DEFOP (trace, 0, 0, 2, 0, 0x0c) +DEFOP (trace_quick, 1, 0, 1, 1, 0x0d) +DEFOP (log_not, 0, 0, 1, 1, 0x0e) +DEFOP (bit_and, 0, 0, 2, 1, 0x0f) +DEFOP (bit_or, 0, 0, 2, 1, 0x10) +DEFOP (bit_xor, 0, 0, 2, 1, 0x11) +DEFOP (bit_not, 0, 0, 1, 1, 0x12) +DEFOP (equal, 0, 0, 2, 1, 0x13) +DEFOP (less_signed, 0, 0, 2, 1, 0x14) +DEFOP (less_unsigned, 0, 0, 2, 1, 0x15) +DEFOP (ext, 1, 0, 1, 1, 0x16) +DEFOP (ref8, 0, 8, 1, 1, 0x17) +DEFOP (ref16, 0, 16, 1, 1, 0x18) +DEFOP (ref32, 0, 32, 1, 1, 0x19) +DEFOP (ref64, 0, 64, 1, 1, 0x1a) +DEFOP (ref_float, 0, 0, 1, 1, 0x1b) +DEFOP (ref_double, 0, 0, 1, 1, 0x1c) +DEFOP (ref_long_double, 0, 0, 1, 1, 0x1d) +DEFOP (l_to_d, 0, 0, 1, 1, 0x1e) +DEFOP (d_to_l, 0, 0, 1, 1, 0x1f) +DEFOP (if_goto, 2, 0, 1, 0, 0x20) +DEFOP (goto, 2, 0, 0, 0, 0x21) +DEFOP (const8, 1, 8, 0, 1, 0x22) +DEFOP (const16, 2, 16, 0, 1, 0x23) +DEFOP (const32, 4, 32, 0, 1, 0x24) +DEFOP (const64, 8, 64, 0, 1, 0x25) +DEFOP (reg, 2, 0, 0, 1, 0x26) +DEFOP (end, 0, 0, 0, 0, 0x27) +DEFOP (dup, 0, 0, 1, 2, 0x28) +DEFOP (pop, 0, 0, 1, 0, 0x29) +DEFOP (zero_ext, 1, 0, 1, 1, 0x2a) +DEFOP (swap, 0, 0, 2, 2, 0x2b) +DEFOP (getv, 2, 0, 0, 1, 0x2c) +DEFOP (setv, 2, 0, 1, 1, 0x2d) +DEFOP (tracev, 2, 0, 0, 1, 0x2e) +DEFOP (tracenz, 0, 0, 2, 0, 0x2f) +DEFOP (trace16, 2, 0, 1, 1, 0x30) +/* We need something here just to make the tables come out ok. */ +DEFOP (invalid2, 0, 0, 0, 0, 0x31) +/* The "consumed" number for pick is wrong, but there's no way to + express the right thing. */ +DEFOP (pick, 1, 0, 0, 1, 0x32) +DEFOP (rot, 0, 0, 3, 3, 0x33) +/* Both the argument and consumed numbers are dynamic for this one. */ +DEFOP (printf, 0, 0, 0, 0, 0x34) diff --git a/gdb/gdbsupport/break-common.h b/gdb/gdbsupport/break-common.h new file mode 100644 index 00000000000..d85cadf442a --- /dev/null +++ b/gdb/gdbsupport/break-common.h @@ -0,0 +1,31 @@ +/* Data structures associated with breakpoints shared in both GDB and + GDBserver. + Copyright (C) 1992-2019 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/>. */ + +#ifndef COMMON_BREAK_COMMON_H +#define COMMON_BREAK_COMMON_H + +enum target_hw_bp_type + { + hw_write = 0, /* Common HW watchpoint */ + hw_read = 1, /* Read HW watchpoint */ + hw_access = 2, /* Access HW watchpoint */ + hw_execute = 3 /* Execute HW breakpoint */ + }; + +#endif /* COMMON_BREAK_COMMON_H */ diff --git a/gdb/gdbsupport/btrace-common.c b/gdb/gdbsupport/btrace-common.c new file mode 100644 index 00000000000..13f1f1a0fdd --- /dev/null +++ b/gdb/gdbsupport/btrace-common.c @@ -0,0 +1,192 @@ +/* Copyright (C) 2014-2019 Free Software Foundation, Inc. + + Contributed by Intel Corp. <markus.t.metzger@intel.com> + + 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 "btrace-common.h" + + +/* See btrace-common.h. */ + +const char * +btrace_format_string (enum btrace_format format) +{ + switch (format) + { + case BTRACE_FORMAT_NONE: + return _("No or unknown format"); + + case BTRACE_FORMAT_BTS: + return _("Branch Trace Store"); + + case BTRACE_FORMAT_PT: + return _("Intel Processor Trace"); + } + + internal_error (__FILE__, __LINE__, _("Unknown branch trace format")); +} + +/* See btrace-common.h. */ + +const char * +btrace_format_short_string (enum btrace_format format) +{ + switch (format) + { + case BTRACE_FORMAT_NONE: + return "unknown"; + + case BTRACE_FORMAT_BTS: + return "bts"; + + case BTRACE_FORMAT_PT: + return "pt"; + } + + internal_error (__FILE__, __LINE__, _("Unknown branch trace format")); +} + +/* See btrace-common.h. */ + +void +btrace_data::fini () +{ + switch (format) + { + case BTRACE_FORMAT_NONE: + /* Nothing to do. */ + return; + + case BTRACE_FORMAT_BTS: + VEC_free (btrace_block_s, variant.bts.blocks); + return; + + case BTRACE_FORMAT_PT: + xfree (variant.pt.data); + return; + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} + +/* See btrace-common.h. */ + +bool +btrace_data::empty () const +{ + switch (format) + { + case BTRACE_FORMAT_NONE: + return true; + + case BTRACE_FORMAT_BTS: + return VEC_empty (btrace_block_s, variant.bts.blocks); + + case BTRACE_FORMAT_PT: + return (variant.pt.size == 0); + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} + +/* See btrace-common.h. */ + +void +btrace_data::clear () +{ + fini (); + format = BTRACE_FORMAT_NONE; +} + +/* See btrace-common.h. */ + +int +btrace_data_append (struct btrace_data *dst, + const struct btrace_data *src) +{ + switch (src->format) + { + case BTRACE_FORMAT_NONE: + return 0; + + case BTRACE_FORMAT_BTS: + switch (dst->format) + { + default: + return -1; + + case BTRACE_FORMAT_NONE: + dst->format = BTRACE_FORMAT_BTS; + dst->variant.bts.blocks = NULL; + + /* Fall-through. */ + case BTRACE_FORMAT_BTS: + { + unsigned int blk; + + /* We copy blocks in reverse order to have the oldest block at + index zero. */ + blk = VEC_length (btrace_block_s, src->variant.bts.blocks); + while (blk != 0) + { + btrace_block_s *block; + + block = VEC_index (btrace_block_s, src->variant.bts.blocks, + --blk); + + VEC_safe_push (btrace_block_s, dst->variant.bts.blocks, block); + } + } + } + return 0; + + case BTRACE_FORMAT_PT: + switch (dst->format) + { + default: + return -1; + + case BTRACE_FORMAT_NONE: + dst->format = BTRACE_FORMAT_PT; + dst->variant.pt.data = NULL; + dst->variant.pt.size = 0; + + /* fall-through. */ + case BTRACE_FORMAT_PT: + { + gdb_byte *data; + size_t size; + + size = src->variant.pt.size + dst->variant.pt.size; + data = (gdb_byte *) xmalloc (size); + + memcpy (data, dst->variant.pt.data, dst->variant.pt.size); + memcpy (data + dst->variant.pt.size, src->variant.pt.data, + src->variant.pt.size); + + xfree (dst->variant.pt.data); + + dst->variant.pt.data = data; + dst->variant.pt.size = size; + } + } + return 0; + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} diff --git a/gdb/gdbsupport/btrace-common.h b/gdb/gdbsupport/btrace-common.h new file mode 100644 index 00000000000..0b18924882c --- /dev/null +++ b/gdb/gdbsupport/btrace-common.h @@ -0,0 +1,257 @@ +/* Branch trace support for GDB, the GNU debugger. + + Copyright (C) 2013-2019 Free Software Foundation, Inc. + + Contributed by Intel Corp. <markus.t.metzger@intel.com>. + + 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/>. */ + +#ifndef COMMON_BTRACE_COMMON_H +#define COMMON_BTRACE_COMMON_H + +/* Branch tracing (btrace) is a per-thread control-flow execution trace of the + inferior. For presentation purposes, the branch trace is represented as a + list of sequential control-flow blocks, one such list per thread. */ + +#include "vec.h" + +/* A branch trace block. + + This represents a block of sequential control-flow. Adjacent blocks will be + connected via calls, returns, or jumps. The latter can be direct or + indirect, conditional or unconditional. Branches can further be + asynchronous, e.g. interrupts. */ +struct btrace_block +{ + /* The address of the first byte of the first instruction in the block. + The address may be zero if we do not know the beginning of this block, + such as for the first block in a delta trace. */ + CORE_ADDR begin; + + /* The address of the first byte of the last instruction in the block. */ + CORE_ADDR end; +}; + +/* Define functions operating on a vector of branch trace blocks. */ +typedef struct btrace_block btrace_block_s; +DEF_VEC_O (btrace_block_s); + +/* Enumeration of btrace formats. */ + +enum btrace_format +{ + /* No branch trace format. */ + BTRACE_FORMAT_NONE, + + /* Branch trace is in Branch Trace Store (BTS) format. + Actually, the format is a sequence of blocks derived from BTS. */ + BTRACE_FORMAT_BTS, + + /* Branch trace is in Intel Processor Trace format. */ + BTRACE_FORMAT_PT +}; + +/* An enumeration of cpu vendors. */ + +enum btrace_cpu_vendor +{ + /* We do not know this vendor. */ + CV_UNKNOWN, + + /* Intel. */ + CV_INTEL +}; + +/* A cpu identifier. */ + +struct btrace_cpu +{ + /* The processor vendor. */ + enum btrace_cpu_vendor vendor; + + /* The cpu family. */ + unsigned short family; + + /* The cpu model. */ + unsigned char model; + + /* The cpu stepping. */ + unsigned char stepping; +}; + +/* A BTS configuration. */ + +struct btrace_config_bts +{ + /* The size of the branch trace buffer in bytes. + + This is unsigned int and not size_t since it is registered as + control variable for "set record btrace bts buffer-size". */ + unsigned int size; +}; + +/* An Intel Processor Trace configuration. */ + +struct btrace_config_pt +{ + /* The size of the branch trace buffer in bytes. + + This is unsigned int and not size_t since it is registered as + control variable for "set record btrace pt buffer-size". */ + unsigned int size; +}; + +/* A branch tracing configuration. + + This describes the requested configuration as well as the actually + obtained configuration. + We describe the configuration for all different formats so we can + easily switch between formats. */ + +struct btrace_config +{ + /* The branch tracing format. */ + enum btrace_format format; + + /* The BTS format configuration. */ + struct btrace_config_bts bts; + + /* The Intel Processor Trace format configuration. */ + struct btrace_config_pt pt; +}; + +/* Branch trace in BTS format. */ +struct btrace_data_bts +{ + /* Branch trace is represented as a vector of branch trace blocks starting + with the most recent block. */ + VEC (btrace_block_s) *blocks; +}; + +/* Configuration information to go with the trace data. */ +struct btrace_data_pt_config +{ + /* The processor on which the trace has been collected. */ + struct btrace_cpu cpu; +}; + +/* Branch trace in Intel Processor Trace format. */ +struct btrace_data_pt +{ + /* Some configuration information to go with the data. */ + struct btrace_data_pt_config config; + + /* The trace data. */ + gdb_byte *data; + + /* The size of DATA in bytes. */ + size_t size; +}; + +/* The branch trace data. */ +struct btrace_data +{ + btrace_data () = default; + + ~btrace_data () + { + fini (); + } + + btrace_data &operator= (btrace_data &&other) + { + if (this != &other) + { + fini (); + format = other.format; + variant = other.variant; + other.format = BTRACE_FORMAT_NONE; + } + return *this; + } + + /* Return true if this is empty; false otherwise. */ + bool empty () const; + + /* Clear this object. */ + void clear (); + + enum btrace_format format = BTRACE_FORMAT_NONE; + + union + { + /* Format == BTRACE_FORMAT_BTS. */ + struct btrace_data_bts bts; + + /* Format == BTRACE_FORMAT_PT. */ + struct btrace_data_pt pt; + } variant; + +private: + + DISABLE_COPY_AND_ASSIGN (btrace_data); + + void fini (); +}; + +/* Target specific branch trace information. */ +struct btrace_target_info; + +/* Enumeration of btrace read types. */ + +enum btrace_read_type +{ + /* Send all available trace. */ + BTRACE_READ_ALL, + + /* Send all available trace, if it changed. */ + BTRACE_READ_NEW, + + /* Send the trace since the last request. This will fail if the trace + buffer overflowed. */ + BTRACE_READ_DELTA +}; + +/* Enumeration of btrace errors. */ + +enum btrace_error +{ + /* No error. Everything is OK. */ + BTRACE_ERR_NONE, + + /* An unknown error. */ + BTRACE_ERR_UNKNOWN, + + /* Branch tracing is not supported on this system. */ + BTRACE_ERR_NOT_SUPPORTED, + + /* The branch trace buffer overflowed; no delta read possible. */ + BTRACE_ERR_OVERFLOW +}; + +/* Return a string representation of FORMAT. */ +extern const char *btrace_format_string (enum btrace_format format); + +/* Return an abbreviation string representation of FORMAT. */ +extern const char *btrace_format_short_string (enum btrace_format format); + +/* Append the branch trace data from SRC to the end of DST. + Both SRC and DST must use the same format. + Returns zero on success; a negative number otherwise. */ +extern int btrace_data_append (struct btrace_data *dst, + const struct btrace_data *src); + +#endif /* COMMON_BTRACE_COMMON_H */ diff --git a/gdb/gdbsupport/buffer.c b/gdb/gdbsupport/buffer.c new file mode 100644 index 00000000000..3c919e70979 --- /dev/null +++ b/gdb/gdbsupport/buffer.c @@ -0,0 +1,178 @@ +/* A simple growing buffer for GDB. + + Copyright (C) 2009-2019 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 "xml-utils.h" +#include "buffer.h" +#include "inttypes.h" +void +buffer_grow (struct buffer *buffer, const char *data, size_t size) +{ + char *new_buffer; + size_t new_buffer_size; + + if (size == 0) + return; + + new_buffer_size = buffer->buffer_size; + + if (new_buffer_size == 0) + new_buffer_size = 1; + + while (buffer->used_size + size > new_buffer_size) + new_buffer_size *= 2; + new_buffer = (char *) xrealloc (buffer->buffer, new_buffer_size); + memcpy (new_buffer + buffer->used_size, data, size); + buffer->buffer = new_buffer; + buffer->buffer_size = new_buffer_size; + buffer->used_size += size; +} + +void +buffer_free (struct buffer *buffer) +{ + if (!buffer) + return; + + xfree (buffer->buffer); + buffer->buffer = NULL; + buffer->buffer_size = 0; + buffer->used_size = 0; +} + +void +buffer_init (struct buffer *buffer) +{ + memset (buffer, 0, sizeof (*buffer)); +} + +char* +buffer_finish (struct buffer *buffer) +{ + char *ret = buffer->buffer; + buffer->buffer = NULL; + buffer->buffer_size = 0; + buffer->used_size = 0; + return ret; +} + +void +buffer_xml_printf (struct buffer *buffer, const char *format, ...) +{ + va_list ap; + const char *f; + const char *prev; + int percent = 0; + + va_start (ap, format); + + prev = format; + for (f = format; *f; f++) + { + if (percent) + { + char buf[32]; + char *str = buf; + const char *f_old = f; + + switch (*f) + { + case 's': + str = va_arg (ap, char *); + break; + case 'd': + sprintf (str, "%d", va_arg (ap, int)); + break; + case 'u': + sprintf (str, "%u", va_arg (ap, unsigned int)); + break; + case 'x': + sprintf (str, "%x", va_arg (ap, unsigned int)); + break; + case 'o': + sprintf (str, "%o", va_arg (ap, unsigned int)); + break; + case 'l': + f++; + switch (*f) + { + case 'd': + sprintf (str, "%ld", va_arg (ap, long)); + break; + case 'u': + sprintf (str, "%lu", va_arg (ap, unsigned long)); + break; + case 'x': + sprintf (str, "%lx", va_arg (ap, unsigned long)); + break; + case 'o': + sprintf (str, "%lo", va_arg (ap, unsigned long)); + break; + case 'l': + f++; + switch (*f) + { + case 'd': + sprintf (str, "%" PRId64, + (int64_t) va_arg (ap, long long)); + break; + case 'u': + sprintf (str, "%" PRIu64, + (uint64_t) va_arg (ap, unsigned long long)); + break; + case 'x': + sprintf (str, "%" PRIx64, + (uint64_t) va_arg (ap, unsigned long long)); + break; + case 'o': + sprintf (str, "%" PRIo64, + (uint64_t) va_arg (ap, unsigned long long)); + break; + default: + str = 0; + break; + } + break; + default: + str = 0; + break; + } + break; + default: + str = 0; + break; + } + + if (str) + { + buffer_grow (buffer, prev, f_old - prev - 1); + std::string p = xml_escape_text (str); + buffer_grow_str (buffer, p.c_str ()); + prev = f + 1; + } + percent = 0; + } + else if (*f == '%') + percent = 1; + } + + buffer_grow_str (buffer, prev); + va_end (ap); +} + diff --git a/gdb/gdbsupport/buffer.h b/gdb/gdbsupport/buffer.h new file mode 100644 index 00000000000..8b24b54d0eb --- /dev/null +++ b/gdb/gdbsupport/buffer.h @@ -0,0 +1,68 @@ +/* A simple growing buffer for GDB. + + Copyright (C) 2009-2019 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/>. */ + +#ifndef COMMON_BUFFER_H +#define COMMON_BUFFER_H + +struct buffer +{ + char *buffer; + size_t buffer_size; /* allocated size */ + size_t used_size; /* actually used size */ +}; + +/* Append DATA of size SIZE to the end of BUFFER. Grows the buffer to + accommodate the new data. */ +void buffer_grow (struct buffer *buffer, const char *data, size_t size); + +/* Append C to the end of BUFFER. Grows the buffer to accommodate the + new data. */ + +static inline void +buffer_grow_char (struct buffer *buffer, char c) +{ + buffer_grow (buffer, &c, 1); +} + +/* Release any memory held by BUFFER. */ +void buffer_free (struct buffer *buffer); + +/* Initialize BUFFER. BUFFER holds no memory afterwards. */ +void buffer_init (struct buffer *buffer); + +/* Return a pointer into BUFFER data, effectively transferring + ownership of the buffer memory to the caller. Calling buffer_free + afterwards has no effect on the returned data. */ +char* buffer_finish (struct buffer *buffer); + +/* Simple printf to buffer function. Current implemented formatters: + %s - grow an xml escaped text in BUFFER. + %d - grow an signed integer in BUFFER. + %u - grow an unsigned integer in BUFFER. + %x - grow an unsigned integer formatted in hexadecimal in BUFFER. + %o - grow an unsigned integer formatted in octal in BUFFER. */ +void buffer_xml_printf (struct buffer *buffer, const char *format, ...) + ATTRIBUTE_PRINTF (2, 3); + +#define buffer_grow_str(BUFFER,STRING) \ + buffer_grow (BUFFER, STRING, strlen (STRING)) +#define buffer_grow_str0(BUFFER,STRING) \ + buffer_grow (BUFFER, STRING, strlen (STRING) + 1) + +#endif /* COMMON_BUFFER_H */ diff --git a/gdb/gdbsupport/byte-vector.h b/gdb/gdbsupport/byte-vector.h new file mode 100644 index 00000000000..5dec3017c20 --- /dev/null +++ b/gdb/gdbsupport/byte-vector.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_BYTE_VECTOR_H +#define COMMON_BYTE_VECTOR_H + +#include "gdbsupport/def-vector.h" + +namespace gdb { + +/* byte_vector is a gdb_byte std::vector with a custom allocator that + unlike std::vector<gdb_byte> does not zero-initialize new elements + by default when the vector is created/resized. This is what you + usually want when working with byte buffers, since if you're + creating or growing a buffer you'll most surely want to fill it in + with data, in which case zero-initialization would be a + pessimization. For example: + + gdb::byte_vector buf (some_large_size); + fill_with_data (buf.data (), buf.size ()); + + On the odd case you do need zero initialization, then you can still + call the overloads that specify an explicit value, like: + + gdb::byte_vector buf (some_initial_size, 0); + buf.resize (a_bigger_size, 0); + + (Or use std::vector<gdb_byte> instead.) + + Note that unlike std::vector<gdb_byte>, function local + gdb::byte_vector objects constructed with an initial size like: + + gdb::byte_vector buf (some_size); + fill_with_data (buf.data (), buf.size ()); + + usually compile down to the exact same as: + + std::unique_ptr<byte[]> buf (new gdb_byte[some_size]); + fill_with_data (buf.get (), some_size); + + with the former having the advantage of being a bit more readable, + and providing the whole std::vector API, if you end up needing it. +*/ +using byte_vector = gdb::def_vector<gdb_byte>; +using char_vector = gdb::def_vector<char>; + +} /* namespace gdb */ + +#endif /* COMMON_DEF_VECTOR_H */ diff --git a/gdb/gdbsupport/cleanups.c b/gdb/gdbsupport/cleanups.c new file mode 100644 index 00000000000..121720d3c0f --- /dev/null +++ b/gdb/gdbsupport/cleanups.c @@ -0,0 +1,144 @@ +/* Cleanup routines for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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 "cleanups.h" + +/* The cleanup list records things that have to be undone + if an error happens (descriptors to be closed, memory to be freed, etc.) + Each link in the chain records a function to call and an + argument to give it. + + Use make_cleanup to add an element to the cleanup chain. + Use do_cleanups to do all cleanup actions back to a given + point in the chain. Use discard_cleanups to remove cleanups + from the chain back to a given point, not doing them. + + If the argument is pointer to allocated memory, then you need + to additionally set the 'free_arg' member to a function that will + free that memory. This function will be called both when the cleanup + is executed and when it's discarded. */ + +struct cleanup +{ + struct cleanup *next; + void (*function) (void *); + void (*free_arg) (void *); + void *arg; +}; + +/* Used to mark the end of a cleanup chain. + The value is chosen so that it: + - is non-NULL so that make_cleanup never returns NULL, + - causes a segv if dereferenced + [though this won't catch errors that a value of, say, + ((struct cleanup *) -1) will] + - displays as something useful when printed in gdb. + This is const for a bit of extra robustness. + It is initialized to coax gcc into putting it into .rodata. + All fields are initialized to survive -Wextra. */ +static const struct cleanup sentinel_cleanup = { 0, 0, 0, 0 }; + +/* Handy macro to use when referring to sentinel_cleanup. */ +#define SENTINEL_CLEANUP ((struct cleanup *) &sentinel_cleanup) + +/* Chain of cleanup actions established with make_final_cleanup, + to be executed when gdb exits. */ +static struct cleanup *final_cleanup_chain = SENTINEL_CLEANUP; + +/* Main worker routine to create a cleanup. + PMY_CHAIN is a pointer to either cleanup_chain or final_cleanup_chain. + FUNCTION is the function to call to perform the cleanup. + ARG is passed to FUNCTION when called. + FREE_ARG, if non-NULL, is called after the cleanup is performed. + + The result is a pointer to the previous chain pointer + to be passed later to do_cleanups or discard_cleanups. */ + +static struct cleanup * +make_my_cleanup2 (struct cleanup **pmy_chain, make_cleanup_ftype *function, + void *arg, void (*free_arg) (void *)) +{ + struct cleanup *newobj = XNEW (struct cleanup); + struct cleanup *old_chain = *pmy_chain; + + newobj->next = *pmy_chain; + newobj->function = function; + newobj->free_arg = free_arg; + newobj->arg = arg; + *pmy_chain = newobj; + + gdb_assert (old_chain != NULL); + return old_chain; +} + +/* Worker routine to create a cleanup without a destructor. + PMY_CHAIN is a pointer to either cleanup_chain or final_cleanup_chain. + FUNCTION is the function to call to perform the cleanup. + ARG is passed to FUNCTION when called. + + The result is a pointer to the previous chain pointer + to be passed later to do_cleanups or discard_cleanups. */ + +static struct cleanup * +make_my_cleanup (struct cleanup **pmy_chain, make_cleanup_ftype *function, + void *arg) +{ + return make_my_cleanup2 (pmy_chain, function, arg, NULL); +} + +/* Add a new cleanup to the final cleanup_chain, + and return the previous chain pointer + to be passed later to do_cleanups or discard_cleanups. + Args are FUNCTION to clean up with, and ARG to pass to it. */ + +struct cleanup * +make_final_cleanup (make_cleanup_ftype *function, void *arg) +{ + return make_my_cleanup (&final_cleanup_chain, function, arg); +} + +/* Worker routine to perform cleanups. + PMY_CHAIN is a pointer to either cleanup_chain or final_cleanup_chain. + OLD_CHAIN is the result of a "make" cleanup routine. + Cleanups are performed until we get back to the old end of the chain. */ + +static void +do_my_cleanups (struct cleanup **pmy_chain, + struct cleanup *old_chain) +{ + struct cleanup *ptr; + + while ((ptr = *pmy_chain) != old_chain) + { + *pmy_chain = ptr->next; /* Do this first in case of recursion. */ + (*ptr->function) (ptr->arg); + if (ptr->free_arg) + (*ptr->free_arg) (ptr->arg); + xfree (ptr); + } +} + +/* Discard final cleanups and do the actions they describe. */ + +void +do_final_cleanups () +{ + do_my_cleanups (&final_cleanup_chain, SENTINEL_CLEANUP); +} diff --git a/gdb/gdbsupport/cleanups.h b/gdb/gdbsupport/cleanups.h new file mode 100644 index 00000000000..e676076a16d --- /dev/null +++ b/gdb/gdbsupport/cleanups.h @@ -0,0 +1,39 @@ +/* Cleanups. + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_CLEANUPS_H +#define COMMON_CLEANUPS_H + +/* Outside of cleanups.c, this is an opaque type. */ +struct cleanup; + +/* NOTE: cagney/2000-03-04: This typedef is strictly for the + make_cleanup function declarations below. Do not use this typedef + as a cast when passing functions into the make_cleanup() code. + Instead either use a bounce function or add a wrapper function. + Calling a f(char*) function with f(void*) is non-portable. */ +typedef void (make_cleanup_ftype) (void *); + +/* Function type for the dtor in make_cleanup_dtor. */ +typedef void (make_cleanup_dtor_ftype) (void *); + +extern struct cleanup *make_final_cleanup (make_cleanup_ftype *, void *); + +extern void do_final_cleanups (); + +#endif /* COMMON_CLEANUPS_H */ diff --git a/gdb/gdbsupport/common-debug.c b/gdb/gdbsupport/common-debug.c new file mode 100644 index 00000000000..97c90032ebd --- /dev/null +++ b/gdb/gdbsupport/common-debug.c @@ -0,0 +1,37 @@ +/* Debug printing functions. + + Copyright (C) 2014-2019 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 "common-debug.h" + +/* See gdbsupport/common-debug.h. */ + +int show_debug_regs; + +/* See gdbsupport/common-debug.h. */ + +void +debug_printf (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + debug_vprintf (fmt, ap); + va_end (ap); +} diff --git a/gdb/gdbsupport/common-debug.h b/gdb/gdbsupport/common-debug.h new file mode 100644 index 00000000000..d5bfc9eb3ae --- /dev/null +++ b/gdb/gdbsupport/common-debug.h @@ -0,0 +1,41 @@ +/* Declarations for debug printing functions. + + Copyright (C) 2014-2019 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/>. */ + +#ifndef COMMON_COMMON_DEBUG_H +#define COMMON_COMMON_DEBUG_H + +/* Set to nonzero to enable debugging of hardware breakpoint/ + watchpoint support code. */ + +extern int show_debug_regs; + +/* Print a formatted message to the appropriate channel for + debugging output for the client. */ + +extern void debug_printf (const char *format, ...) + ATTRIBUTE_PRINTF (1, 2); + +/* Print a formatted message to the appropriate channel for + debugging output for the client. This function must be + provided by the client. */ + +extern void debug_vprintf (const char *format, va_list ap) + ATTRIBUTE_PRINTF (1, 0); + +#endif /* COMMON_COMMON_DEBUG_H */ diff --git a/gdb/gdbsupport/common-defs.h b/gdb/gdbsupport/common-defs.h new file mode 100644 index 00000000000..203bd8972da --- /dev/null +++ b/gdb/gdbsupport/common-defs.h @@ -0,0 +1,150 @@ +/* Common definitions. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_COMMON_DEFS_H +#define COMMON_COMMON_DEFS_H + +#include "config.h" + +#undef PACKAGE_NAME +#undef PACKAGE_VERSION +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME + +#ifdef GDBSERVER +#include "build-gnulib-gdbserver/config.h" +#else +#include "../../gnulib/config.h" +#endif + +#undef PACKAGE_NAME +#undef PACKAGE_VERSION +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME + +/* From: + https://www.gnu.org/software/gnulib/manual/html_node/stdint_002eh.html + + "On some hosts that predate C++11, when using C++ one must define + __STDC_CONSTANT_MACROS to make visible the definitions of constant + macros such as INTMAX_C, and one must define __STDC_LIMIT_MACROS to + make visible the definitions of limit macros such as INTMAX_MAX.". + + And: + https://www.gnu.org/software/gnulib/manual/html_node/inttypes_002eh.html + + "On some hosts that predate C++11, when using C++ one must define + __STDC_FORMAT_MACROS to make visible the declarations of format + macros such as PRIdMAX." + + Must do this before including any system header, since other system + headers may include stdint.h/inttypes.h. */ +#define __STDC_CONSTANT_MACROS 1 +#define __STDC_LIMIT_MACROS 1 +#define __STDC_FORMAT_MACROS 1 + +/* Some distros enable _FORTIFY_SOURCE by default, which on occasion + has caused build failures with -Wunused-result when a patch is + developed on a distro that does not enable _FORTIFY_SOURCE. We + enable it here in order to try to catch these problems earlier; + plus this seems like a reasonable safety measure. The check for + optimization is required because _FORTIFY_SOURCE only works when + optimization is enabled. If _FORTIFY_SOURCE is already defined, + then we don't do anything. */ + +#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ > 0 +#define _FORTIFY_SOURCE 2 +#endif + +/* We don't support Windows versions before XP, so we define + _WIN32_WINNT correspondingly to ensure the Windows API headers + expose the required symbols. */ +#if defined (__MINGW32__) || defined (__CYGWIN__) +# ifdef _WIN32_WINNT +# if _WIN32_WINNT < 0x0501 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +# endif +# else +# define _WIN32_WINNT 0x0501 +# endif +#endif /* __MINGW32__ || __CYGWIN__ */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> /* for strcasecmp and strncasecmp */ +#endif +#include <errno.h> +#include <alloca.h> + +#include "ansidecl.h" +/* This is defined by ansidecl.h, but we prefer gnulib's version. On + MinGW, gnulib might enable __USE_MINGW_ANSI_STDIO, which may or not + require use of attribute gnu_printf instead of printf. gnulib + checks that at configure time. Since _GL_ATTRIBUTE_FORMAT_PRINTF + is compatible with ATTRIBUTE_PRINTF, simply use it. */ +#undef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF _GL_ATTRIBUTE_FORMAT_PRINTF + +#if GCC_VERSION >= 3004 +#define ATTRIBUTE_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) +#else +#define ATTRIBUTE_UNUSED_RESULT +#endif + +#include "libiberty.h" +#include "pathmax.h" +#include "gdb/signals.h" +#include "gdb_locale.h" +#include "ptid.h" +#include "common-types.h" +#include "common-utils.h" +#include "gdb_assert.h" +#include "errors.h" +#include "print-utils.h" +#include "common-debug.h" +#include "cleanups.h" +#include "common-exceptions.h" +#include "gdbsupport/poison.h" + +#define EXTERN_C extern "C" +#define EXTERN_C_PUSH extern "C" { +#define EXTERN_C_POP } + +/* Pull in gdb::unique_xmalloc_ptr. */ +#include "gdbsupport/gdb_unique_ptr.h" + +/* String containing the current directory (what getwd would return). */ +extern char *current_directory; + +/* sbrk on macOS is not useful for our purposes, since sbrk(0) always + returns the same value. brk/sbrk on macOS is just an emulation + that always returns a pointer to a 4MB section reserved for + that. */ + +#if defined (HAVE_SBRK) && !__APPLE__ +#define HAVE_USEFUL_SBRK 1 +#endif + +#endif /* COMMON_COMMON_DEFS_H */ diff --git a/gdb/gdbsupport/common-exceptions.c b/gdb/gdbsupport/common-exceptions.c new file mode 100644 index 00000000000..9f210250a6f --- /dev/null +++ b/gdb/gdbsupport/common-exceptions.c @@ -0,0 +1,235 @@ +/* Exception (throw catch) mechanism, for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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 "common-exceptions.h" +#include <forward_list> + +/* Possible catcher states. */ +enum catcher_state { + /* Initial state, a new catcher has just been created. */ + CATCHER_CREATED, + /* The catch code is running. */ + CATCHER_RUNNING, + CATCHER_RUNNING_1, + /* The catch code threw an exception. */ + CATCHER_ABORTING +}; + +/* Possible catcher actions. */ +enum catcher_action { + CATCH_ITER, + CATCH_ITER_1, + CATCH_THROWING +}; + +struct catcher +{ + enum catcher_state state = CATCHER_CREATED; + /* Jump buffer pointing back at the exception handler. */ + jmp_buf buf; + /* Status buffer belonging to the exception handler. */ + struct gdb_exception exception; +}; + +/* Where to go for throw_exception(). */ +static std::forward_list<struct catcher> catchers; + +jmp_buf * +exceptions_state_mc_init () +{ + catchers.emplace_front (); + return &catchers.front ().buf; +} + +/* Catcher state machine. Returns non-zero if the m/c should be run + again, zero if it should abort. */ + +static int +exceptions_state_mc (enum catcher_action action) +{ + switch (catchers.front ().state) + { + case CATCHER_CREATED: + switch (action) + { + case CATCH_ITER: + /* Allow the code to run the catcher. */ + catchers.front ().state = CATCHER_RUNNING; + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad state")); + } + case CATCHER_RUNNING: + switch (action) + { + case CATCH_ITER: + /* No error/quit has occured. */ + return 0; + case CATCH_ITER_1: + catchers.front ().state = CATCHER_RUNNING_1; + return 1; + case CATCH_THROWING: + catchers.front ().state = CATCHER_ABORTING; + /* See also throw_exception. */ + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } + case CATCHER_RUNNING_1: + switch (action) + { + case CATCH_ITER: + /* The did a "break" from the inner while loop. */ + return 0; + case CATCH_ITER_1: + catchers.front ().state = CATCHER_RUNNING; + return 0; + case CATCH_THROWING: + catchers.front ().state = CATCHER_ABORTING; + /* See also throw_exception. */ + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } + case CATCHER_ABORTING: + switch (action) + { + case CATCH_ITER: + { + /* Exit normally if this catcher can handle this + exception. The caller analyses the func return + values. */ + return 0; + } + default: + internal_error (__FILE__, __LINE__, _("bad state")); + } + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } +} + +int +exceptions_state_mc_catch (struct gdb_exception *exception, + int mask) +{ + *exception = std::move (catchers.front ().exception); + catchers.pop_front (); + + if (exception->reason < 0) + { + if (mask & RETURN_MASK (exception->reason)) + { + /* Exit normally and let the caller handle the + exception. */ + return 1; + } + + /* The caller didn't request that the event be caught, relay the + event to the next exception_catch/CATCH_SJLJ. */ + throw_exception_sjlj (*exception); + } + + /* No exception was thrown. */ + return 0; +} + +int +exceptions_state_mc_action_iter (void) +{ + return exceptions_state_mc (CATCH_ITER); +} + +int +exceptions_state_mc_action_iter_1 (void) +{ + return exceptions_state_mc (CATCH_ITER_1); +} + +/* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */ + +void +throw_exception_sjlj (const struct gdb_exception &exception) +{ + /* Jump to the nearest CATCH_SJLJ block, communicating REASON to + that call via setjmp's return value. Note that REASON can't be + zero, by definition in common-exceptions.h. */ + exceptions_state_mc (CATCH_THROWING); + enum return_reason reason = exception.reason; + catchers.front ().exception = exception; + longjmp (catchers.front ().buf, reason); +} + +/* Implementation of throw_exception that uses C++ try/catch. */ + +void +throw_exception (gdb_exception &&exception) +{ + if (exception.reason == RETURN_QUIT) + throw gdb_exception_quit (std::move (exception)); + else if (exception.reason == RETURN_ERROR) + throw gdb_exception_error (std::move (exception)); + else + gdb_assert_not_reached ("invalid return reason"); +} + +static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) +throw_it (enum return_reason reason, enum errors error, const char *fmt, + va_list ap) +{ + if (reason == RETURN_QUIT) + throw gdb_exception_quit (fmt, ap); + else if (reason == RETURN_ERROR) + throw gdb_exception_error (error, fmt, ap); + else + gdb_assert_not_reached ("invalid return reason"); +} + +void +throw_verror (enum errors error, const char *fmt, va_list ap) +{ + throw_it (RETURN_ERROR, error, fmt, ap); +} + +void +throw_vquit (const char *fmt, va_list ap) +{ + throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); +} + +void +throw_error (enum errors error, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + throw_verror (error, fmt, args); + va_end (args); +} + +void +throw_quit (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + throw_vquit (fmt, args); + va_end (args); +} diff --git a/gdb/gdbsupport/common-exceptions.h b/gdb/gdbsupport/common-exceptions.h new file mode 100644 index 00000000000..ebcaf031354 --- /dev/null +++ b/gdb/gdbsupport/common-exceptions.h @@ -0,0 +1,304 @@ +/* Exception (throw catch) mechanism, for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_COMMON_EXCEPTIONS_H +#define COMMON_COMMON_EXCEPTIONS_H + +#include <setjmp.h> +#include <new> +#include <memory> +#include <string> + +/* Reasons for calling throw_exceptions(). NOTE: all reason values + must be different from zero. enum value 0 is reserved for internal + use as the return value from an initial setjmp(). */ + +enum return_reason + { + /* User interrupt. */ + RETURN_QUIT = -2, + /* Any other error. */ + RETURN_ERROR + }; + +#define RETURN_MASK(reason) (1 << (int)(-reason)) + +typedef enum +{ + RETURN_MASK_QUIT = RETURN_MASK (RETURN_QUIT), + RETURN_MASK_ERROR = RETURN_MASK (RETURN_ERROR), + RETURN_MASK_ALL = (RETURN_MASK_QUIT | RETURN_MASK_ERROR) +} return_mask; + +/* Describe all exceptions. */ + +enum errors { + GDB_NO_ERROR, + + /* Any generic error, the corresponding text is in + exception.message. */ + GENERIC_ERROR, + + /* Something requested was not found. */ + NOT_FOUND_ERROR, + + /* Thread library lacks support necessary for finding thread local + storage. */ + TLS_NO_LIBRARY_SUPPORT_ERROR, + + /* Load module not found while attempting to find thread local storage. */ + TLS_LOAD_MODULE_NOT_FOUND_ERROR, + + /* Thread local storage has not been allocated yet. */ + TLS_NOT_ALLOCATED_YET_ERROR, + + /* Something else went wrong while attempting to find thread local + storage. The ``struct gdb_exception'' message field provides + more detail. */ + TLS_GENERIC_ERROR, + + /* Problem parsing an XML document. */ + XML_PARSE_ERROR, + + /* Error accessing memory. */ + MEMORY_ERROR, + + /* Value not available. E.g., a register was not collected in a + traceframe. */ + NOT_AVAILABLE_ERROR, + + /* Value was optimized out. Note: if the value was a register, this + means the register was not saved in the frame. */ + OPTIMIZED_OUT_ERROR, + + /* DW_OP_entry_value resolving failed. */ + NO_ENTRY_VALUE_ERROR, + + /* Target throwing an error has been closed. Current command should be + aborted as the inferior state is no longer valid. */ + TARGET_CLOSE_ERROR, + + /* An undefined command was executed. */ + UNDEFINED_COMMAND_ERROR, + + /* Requested feature, method, mechanism, etc. is not supported. */ + NOT_SUPPORTED_ERROR, + + /* The number of candidates generated during line completion has + reached the user's specified limit. This isn't an error, this exception + is used to halt searching for more completions, but for consistency + "_ERROR" is appended to the name. */ + MAX_COMPLETIONS_REACHED_ERROR, + + /* Add more errors here. */ + NR_ERRORS +}; + +struct gdb_exception +{ + gdb_exception () + : reason ((enum return_reason) 0), + error (GDB_NO_ERROR) + { + } + + gdb_exception (enum return_reason r, enum errors e) + : reason (r), + error (e) + { + } + + gdb_exception (enum return_reason r, enum errors e, + const char *fmt, va_list ap) + ATTRIBUTE_PRINTF (4, 0) + : reason (r), + error (e), + message (std::make_shared<std::string> (string_vprintf (fmt, ap))) + { + } + + /* The move constructor exists so that we can mark it "noexcept", + which is a good practice for any sort of exception object. */ + explicit gdb_exception (gdb_exception &&other) noexcept = default; + + /* The copy constructor exists so that we can mark it "noexcept", + which is a good practice for any sort of exception object. */ + gdb_exception (const gdb_exception &other) noexcept + : reason (other.reason), + error (other.error), + message (other.message) + { + } + + /* The assignment operator exists so that we can mark it "noexcept", + which is a good practice for any sort of exception object. */ + gdb_exception &operator= (const gdb_exception &other) noexcept + { + reason = other.reason; + error = other.error; + message = other.message; + return *this; + } + + gdb_exception &operator= (gdb_exception &&other) noexcept = default; + + /* Return the contents of the exception message, as a C string. The + string remains owned by the exception object. */ + const char *what () const noexcept + { + return message->c_str (); + } + + enum return_reason reason; + enum errors error; + std::shared_ptr<std::string> message; +}; + +/* Functions to drive the sjlj-based exceptions state machine. Though + declared here by necessity, these functions should be considered + internal to the exceptions subsystem and not used other than via + the TRY/CATCH (or TRY_SJLJ/CATCH_SJLJ) macros defined below. */ + +extern jmp_buf *exceptions_state_mc_init (void); +extern int exceptions_state_mc_action_iter (void); +extern int exceptions_state_mc_action_iter_1 (void); +extern int exceptions_state_mc_catch (struct gdb_exception *, int); + +/* Macro to wrap up standard try/catch behavior. + + The double loop lets us correctly handle code "break"ing out of the + try catch block. (It works as the "break" only exits the inner + "while" loop, the outer for loop detects this handling it + correctly.) Of course "return" and "goto" are not so lucky. + + For instance: + + *INDENT-OFF* + + TRY_SJLJ + { + } + CATCH_SJLJ (e, RETURN_MASK_ERROR) + { + switch (e.reason) + { + case RETURN_ERROR: ... + } + } + END_CATCH_SJLJ + + The SJLJ variants are needed in some cases where gdb exceptions + need to cross third-party library code compiled without exceptions + support (e.g., readline). */ + +#define TRY_SJLJ \ + { \ + jmp_buf *buf = \ + exceptions_state_mc_init (); \ + setjmp (*buf); \ + } \ + while (exceptions_state_mc_action_iter ()) \ + while (exceptions_state_mc_action_iter_1 ()) + +#define CATCH_SJLJ(EXCEPTION, MASK) \ + { \ + struct gdb_exception EXCEPTION; \ + if (exceptions_state_mc_catch (&(EXCEPTION), MASK)) + +#define END_CATCH_SJLJ \ + } + +/* The exception types client code may catch. They're just shims + around gdb_exception that add nothing but type info. Which is used + is selected depending on the MASK argument passed to CATCH. */ + +struct gdb_exception_error : public gdb_exception +{ + gdb_exception_error (enum errors e, const char *fmt, va_list ap) + ATTRIBUTE_PRINTF (3, 0) + : gdb_exception (RETURN_ERROR, e, fmt, ap) + { + } + + explicit gdb_exception_error (gdb_exception &&ex) noexcept + : gdb_exception (std::move (ex)) + { + gdb_assert (ex.reason == RETURN_ERROR); + } +}; + +struct gdb_exception_quit : public gdb_exception +{ + gdb_exception_quit (const char *fmt, va_list ap) + ATTRIBUTE_PRINTF (2, 0) + : gdb_exception (RETURN_QUIT, GDB_NO_ERROR, fmt, ap) + { + } + + explicit gdb_exception_quit (gdb_exception &&ex) noexcept + : gdb_exception (std::move (ex)) + { + gdb_assert (ex.reason == RETURN_QUIT); + } +}; + +/* An exception type that inherits from both std::bad_alloc and a gdb + exception. This is necessary because operator new can only throw + std::bad_alloc, and OTOH, we want exceptions thrown due to memory + allocation error to be caught by all the CATCH/RETURN_MASK_ALL + spread around the codebase. */ + +struct gdb_quit_bad_alloc + : public gdb_exception_quit, + public std::bad_alloc +{ + explicit gdb_quit_bad_alloc (gdb_exception &&ex) noexcept + : gdb_exception_quit (std::move (ex)), + std::bad_alloc () + { + } +}; + +/* *INDENT-ON* */ + +/* Throw an exception (as described by "struct gdb_exception"), + landing in the inner most containing exception handler established + using TRY/CATCH. */ +extern void throw_exception (gdb_exception &&exception) + ATTRIBUTE_NORETURN; + +/* Throw an exception by executing a LONG JUMP to the inner most + containing exception handler established using TRY_SJLJ. Necessary + in some cases where we need to throw GDB exceptions across + third-party library code (e.g., readline). */ +extern void throw_exception_sjlj (const struct gdb_exception &exception) + ATTRIBUTE_NORETURN; + +/* Convenience wrappers around throw_exception that throw GDB + errors. */ +extern void throw_verror (enum errors, const char *fmt, va_list ap) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0); +extern void throw_vquit (const char *fmt, va_list ap) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0); +extern void throw_error (enum errors error, const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); +extern void throw_quit (const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2); + +#endif /* COMMON_COMMON_EXCEPTIONS_H */ diff --git a/gdb/gdbsupport/common-gdbthread.h b/gdb/gdbsupport/common-gdbthread.h new file mode 100644 index 00000000000..d692be209cd --- /dev/null +++ b/gdb/gdbsupport/common-gdbthread.h @@ -0,0 +1,25 @@ +/* Common multi-process/thread control defs for GDB and gdbserver. + Copyright (C) 1987-2019 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/>. */ + +#ifndef COMMON_COMMON_GDBTHREAD_H +#define COMMON_COMMON_GDBTHREAD_H + +/* Switch from one thread to another. */ +extern void switch_to_thread (ptid_t ptid); + +#endif /* COMMON_COMMON_GDBTHREAD_H */ diff --git a/gdb/gdbsupport/common-inferior.h b/gdb/gdbsupport/common-inferior.h new file mode 100644 index 00000000000..72e4bd9eac3 --- /dev/null +++ b/gdb/gdbsupport/common-inferior.h @@ -0,0 +1,41 @@ +/* Functions to deal with the inferior being executed on GDB or + GDBserver. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_COMMON_INFERIOR_H +#define COMMON_COMMON_INFERIOR_H + +/* Return the exec wrapper to be used when starting the inferior, or NULL + otherwise. */ +extern const char *get_exec_wrapper (); + +/* Return the name of the executable file as a string. + ERR nonzero means get error if there is none specified; + otherwise return 0 in that case. */ +extern char *get_exec_file (int err); + +/* Return the inferior's current working directory. If nothing has + been set, then return NULL. */ +extern const char *get_inferior_cwd (); + +/* Set the inferior current working directory. If CWD is NULL, unset + the directory. */ +extern void set_inferior_cwd (const char *cwd); + +#endif /* COMMON_COMMON_INFERIOR_H */ diff --git a/gdb/gdbsupport/common-regcache.c b/gdb/gdbsupport/common-regcache.c new file mode 100644 index 00000000000..4bdadffb824 --- /dev/null +++ b/gdb/gdbsupport/common-regcache.c @@ -0,0 +1,36 @@ +/* Cache and manage the values of registers for GDB, the GNU debugger. + + Copyright (C) 2015-2019 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 "common-regcache.h" + +/* Return the register's value or throw if it's not available. */ + +ULONGEST +regcache_raw_get_unsigned (struct regcache *regcache, int regnum) +{ + ULONGEST value; + enum register_status status; + + status = regcache_raw_read_unsigned (regcache, regnum, &value); + if (status == REG_UNAVAILABLE) + throw_error (NOT_AVAILABLE_ERROR, + _("Register %d is not available"), regnum); + return value; +} diff --git a/gdb/gdbsupport/common-regcache.h b/gdb/gdbsupport/common-regcache.h new file mode 100644 index 00000000000..95ce64a91f9 --- /dev/null +++ b/gdb/gdbsupport/common-regcache.h @@ -0,0 +1,85 @@ +/* Cache and manage the values of registers + + Copyright (C) 2014-2019 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/>. */ + +#ifndef COMMON_COMMON_REGCACHE_H +#define COMMON_COMMON_REGCACHE_H + +/* This header is a stopgap until we have an independent regcache. */ + +enum register_status : signed char + { + /* The register value is not in the cache, and we don't know yet + whether it's available in the target (or traceframe). */ + REG_UNKNOWN = 0, + + /* The register value is valid and cached. */ + REG_VALID = 1, + + /* The register value is unavailable. E.g., we're inspecting a + traceframe, and this register wasn't collected. Note that this + is different a different "unavailable" from saying the register + does not exist in the target's architecture --- in that case, + the target should have given us a target description that does + not include the register in the first place. */ + REG_UNAVAILABLE = -1 + }; + +/* Return a pointer to the register cache associated with the + thread specified by PTID. This function must be provided by + the client. */ + +extern struct regcache *get_thread_regcache_for_ptid (ptid_t ptid); + +/* Return the size of register numbered N in REGCACHE. This function + must be provided by the client. */ + +extern int regcache_register_size (const struct regcache *regcache, int n); + +/* Read the PC register. This function must be provided by the + client. */ + +extern CORE_ADDR regcache_read_pc (struct regcache *regcache); + +/* Read a raw register into a unsigned integer. */ +extern enum register_status regcache_raw_read_unsigned + (struct regcache *regcache, int regnum, ULONGEST *val); + +ULONGEST regcache_raw_get_unsigned (struct regcache *regcache, int regnum); + +struct reg_buffer_common +{ + virtual ~reg_buffer_common () = default; + + /* Get the availability status of the value of register REGNUM in this + buffer. */ + virtual register_status get_register_status (int regnum) const = 0; + + /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE. */ + virtual void raw_supply (int regnum, const void *buf) = 0; + + /* Collect register REGNUM from REGCACHE and store its contents in BUF. */ + virtual void raw_collect (int regnum, void *buf) const = 0; + + /* Compare the contents of the register stored in the regcache (ignoring the + first OFFSET bytes) to the contents of BUF (without any offset). Returns + true if the same. */ + virtual bool raw_compare (int regnum, const void *buf, int offset) const = 0; +}; + +#endif /* COMMON_COMMON_REGCACHE_H */ diff --git a/gdb/gdbsupport/common-types.h b/gdb/gdbsupport/common-types.h new file mode 100644 index 00000000000..ed73f6f990a --- /dev/null +++ b/gdb/gdbsupport/common-types.h @@ -0,0 +1,66 @@ +/* Declarations for common types. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_COMMON_TYPES_H +#define COMMON_COMMON_TYPES_H + +#ifdef GDBSERVER + +/* * A byte from the program being debugged. */ +typedef unsigned char gdb_byte; + +typedef unsigned long long CORE_ADDR; + +typedef long long LONGEST; +typedef unsigned long long ULONGEST; + +#else /* GDBSERVER */ + +#include "bfd.h" + +/* * A byte from the program being debugged. */ +typedef bfd_byte gdb_byte; + +/* * An address in the program being debugged. Host byte order. */ +typedef bfd_vma CORE_ADDR; + +/* This is to make sure that LONGEST is at least as big as CORE_ADDR. */ + +#ifdef BFD64 + +typedef BFD_HOST_64_BIT LONGEST; +typedef BFD_HOST_U_64_BIT ULONGEST; + +#else /* No BFD64 */ + +typedef long long LONGEST; +typedef unsigned long long ULONGEST; + +#endif /* No BFD64 */ +#endif /* GDBSERVER */ + +/* * The largest CORE_ADDR value. */ +#define CORE_ADDR_MAX (~(CORE_ADDR) 0) + +/* * The largest ULONGEST value. */ +#define ULONGEST_MAX (~(ULONGEST) 0) + +enum tribool { TRIBOOL_UNKNOWN = -1, TRIBOOL_FALSE = 0, TRIBOOL_TRUE = 1 }; + +#endif /* COMMON_COMMON_TYPES_H */ diff --git a/gdb/gdbsupport/common-utils.c b/gdb/gdbsupport/common-utils.c new file mode 100644 index 00000000000..384029db70a --- /dev/null +++ b/gdb/gdbsupport/common-utils.c @@ -0,0 +1,358 @@ +/* Shared general utility routines for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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 "common-utils.h" +#include "host-defs.h" +#include <ctype.h> + +void * +xzalloc (size_t size) +{ + return xcalloc (1, size); +} + +/* Like asprintf/vasprintf but get an internal_error if the call + fails. */ + +char * +xstrprintf (const char *format, ...) +{ + char *ret; + va_list args; + + va_start (args, format); + ret = xstrvprintf (format, args); + va_end (args); + return ret; +} + +char * +xstrvprintf (const char *format, va_list ap) +{ + char *ret = NULL; + int status = vasprintf (&ret, format, ap); + + /* NULL is returned when there was a memory allocation problem, or + any other error (for instance, a bad format string). A negative + status (the printed length) with a non-NULL buffer should never + happen, but just to be sure. */ + if (ret == NULL || status < 0) + internal_error (__FILE__, __LINE__, _("vasprintf call failed")); + return ret; +} + +int +xsnprintf (char *str, size_t size, const char *format, ...) +{ + va_list args; + int ret; + + va_start (args, format); + ret = vsnprintf (str, size, format, args); + gdb_assert (ret < size); + va_end (args); + + return ret; +} + +/* See documentation in common-utils.h. */ + +std::string +string_printf (const char* fmt, ...) +{ + va_list vp; + int size; + + va_start (vp, fmt); + size = vsnprintf (NULL, 0, fmt, vp); + va_end (vp); + + std::string str (size, '\0'); + + /* C++11 and later guarantee std::string uses contiguous memory and + always includes the terminating '\0'. */ + va_start (vp, fmt); + vsprintf (&str[0], fmt, vp); + va_end (vp); + + return str; +} + +/* See documentation in common-utils.h. */ + +std::string +string_vprintf (const char* fmt, va_list args) +{ + va_list vp; + size_t size; + + va_copy (vp, args); + size = vsnprintf (NULL, 0, fmt, vp); + va_end (vp); + + std::string str (size, '\0'); + + /* C++11 and later guarantee std::string uses contiguous memory and + always includes the terminating '\0'. */ + vsprintf (&str[0], fmt, args); + + return str; +} + + +/* See documentation in common-utils.h. */ + +void +string_appendf (std::string &str, const char *fmt, ...) +{ + va_list vp; + + va_start (vp, fmt); + string_vappendf (str, fmt, vp); + va_end (vp); +} + + +/* See documentation in common-utils.h. */ + +void +string_vappendf (std::string &str, const char *fmt, va_list args) +{ + va_list vp; + int grow_size; + + va_copy (vp, args); + grow_size = vsnprintf (NULL, 0, fmt, vp); + va_end (vp); + + size_t curr_size = str.size (); + str.resize (curr_size + grow_size); + + /* C++11 and later guarantee std::string uses contiguous memory and + always includes the terminating '\0'. */ + vsprintf (&str[curr_size], fmt, args); +} + +char * +savestring (const char *ptr, size_t len) +{ + char *p = (char *) xmalloc (len + 1); + + memcpy (p, ptr, len); + p[len] = 0; + return p; +} + +/* The bit offset of the highest byte in a ULONGEST, for overflow + checking. */ + +#define HIGH_BYTE_POSN ((sizeof (ULONGEST) - 1) * HOST_CHAR_BIT) + +/* True (non-zero) iff DIGIT is a valid digit in radix BASE, + where 2 <= BASE <= 36. */ + +static int +is_digit_in_base (unsigned char digit, int base) +{ + if (!isalnum (digit)) + return 0; + if (base <= 10) + return (isdigit (digit) && digit < base + '0'); + else + return (isdigit (digit) || tolower (digit) < base - 10 + 'a'); +} + +static int +digit_to_int (unsigned char c) +{ + if (isdigit (c)) + return c - '0'; + else + return tolower (c) - 'a' + 10; +} + +/* As for strtoul, but for ULONGEST results. */ + +ULONGEST +strtoulst (const char *num, const char **trailer, int base) +{ + unsigned int high_part; + ULONGEST result; + int minus = 0; + int i = 0; + + /* Skip leading whitespace. */ + while (isspace (num[i])) + i++; + + /* Handle prefixes. */ + if (num[i] == '+') + i++; + else if (num[i] == '-') + { + minus = 1; + i++; + } + + if (base == 0 || base == 16) + { + if (num[i] == '0' && (num[i + 1] == 'x' || num[i + 1] == 'X')) + { + i += 2; + if (base == 0) + base = 16; + } + } + + if (base == 0 && num[i] == '0') + base = 8; + + if (base == 0) + base = 10; + + if (base < 2 || base > 36) + { + errno = EINVAL; + return 0; + } + + result = high_part = 0; + for (; is_digit_in_base (num[i], base); i += 1) + { + result = result * base + digit_to_int (num[i]); + high_part = high_part * base + (unsigned int) (result >> HIGH_BYTE_POSN); + result &= ((ULONGEST) 1 << HIGH_BYTE_POSN) - 1; + if (high_part > 0xff) + { + errno = ERANGE; + result = ~ (ULONGEST) 0; + high_part = 0; + minus = 0; + break; + } + } + + if (trailer != NULL) + *trailer = &num[i]; + + result = result + ((ULONGEST) high_part << HIGH_BYTE_POSN); + if (minus) + return -result; + else + return result; +} + +/* See documentation in common-utils.h. */ + +char * +skip_spaces (char *chp) +{ + if (chp == NULL) + return NULL; + while (*chp && isspace (*chp)) + chp++; + return chp; +} + +/* A const-correct version of the above. */ + +const char * +skip_spaces (const char *chp) +{ + if (chp == NULL) + return NULL; + while (*chp && isspace (*chp)) + chp++; + return chp; +} + +/* See documentation in common-utils.h. */ + +const char * +skip_to_space (const char *chp) +{ + if (chp == NULL) + return NULL; + while (*chp && !isspace (*chp)) + chp++; + return chp; +} + +/* See documentation in common-utils.h. */ + +char * +skip_to_space (char *chp) +{ + return (char *) skip_to_space ((const char *) chp); +} + +/* See gdbsupport/common-utils.h. */ + +void +free_vector_argv (std::vector<char *> &v) +{ + for (char *el : v) + xfree (el); + + v.clear (); +} + +/* See gdbsupport/common-utils.h. */ + +std::string +stringify_argv (const std::vector<char *> &args) +{ + std::string ret; + + if (!args.empty () && args[0] != NULL) + { + for (auto s : args) + if (s != NULL) + { + ret += s; + ret += ' '; + } + + /* Erase the last whitespace. */ + ret.erase (ret.end () - 1); + } + + return ret; +} + +/* See gdbsupport/common-utils.h. */ + +ULONGEST +align_up (ULONGEST v, int n) +{ + /* Check that N is really a power of two. */ + gdb_assert (n && (n & (n-1)) == 0); + return (v + n - 1) & -n; +} + +/* See gdbsupport/common-utils.h. */ + +ULONGEST +align_down (ULONGEST v, int n) +{ + /* Check that N is really a power of two. */ + gdb_assert (n && (n & (n-1)) == 0); + return (v & -n); +} diff --git a/gdb/gdbsupport/common-utils.h b/gdb/gdbsupport/common-utils.h new file mode 100644 index 00000000000..52bf3437b1c --- /dev/null +++ b/gdb/gdbsupport/common-utils.h @@ -0,0 +1,181 @@ +/* Shared general utility routines for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_COMMON_UTILS_H +#define COMMON_COMMON_UTILS_H + +#include <string> +#include <vector> + +#include "poison.h" + +/* If possible, define FUNCTION_NAME, a macro containing the name of + the function being defined. Since this macro may not always be + defined, all uses must be protected by appropriate macro definition + checks (Eg: "#ifdef FUNCTION_NAME"). + + Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__' + which contains the name of the function currently being defined. + This is broken in G++ before version 2.6. + C9x has a similar variable called __func__, but prefer the GCC one since + it demangles C++ function names. */ +#if (GCC_VERSION >= 2004) +#define FUNCTION_NAME __PRETTY_FUNCTION__ +#else +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#define FUNCTION_NAME __func__ /* ARI: func */ +#endif +#endif + +/* xmalloc(), xrealloc() and xcalloc() have already been declared in + "libiberty.h". */ + +/* Like xmalloc, but zero the memory. */ +void *xzalloc (size_t); + +template <typename T> +static void +xfree (T *ptr) +{ + static_assert (IsFreeable<T>::value, "Trying to use xfree with a non-POD \ +data type. Use operator delete instead."); + + if (ptr != NULL) + free (ptr); /* ARI: free */ +} + + +/* Like asprintf and vasprintf, but return the string, throw an error + if no memory. */ +char *xstrprintf (const char *format, ...) ATTRIBUTE_PRINTF (1, 2); +char *xstrvprintf (const char *format, va_list ap) + ATTRIBUTE_PRINTF (1, 0); + +/* Like snprintf, but throw an error if the output buffer is too small. */ +int xsnprintf (char *str, size_t size, const char *format, ...) + ATTRIBUTE_PRINTF (3, 4); + +/* Returns a std::string built from a printf-style format string. */ +std::string string_printf (const char* fmt, ...) + ATTRIBUTE_PRINTF (1, 2); + +/* Like string_printf, but takes a va_list. */ +std::string string_vprintf (const char* fmt, va_list args) + ATTRIBUTE_PRINTF (1, 0); + +/* Like string_printf, but appends to DEST instead of returning a new + std::string. */ +void string_appendf (std::string &dest, const char* fmt, ...) + ATTRIBUTE_PRINTF (2, 3); + +/* Like string_appendf, but takes a va_list. */ +void string_vappendf (std::string &dest, const char* fmt, va_list args) + ATTRIBUTE_PRINTF (2, 0); + +/* Make a copy of the string at PTR with LEN characters + (and add a null character at the end in the copy). + Uses malloc to get the space. Returns the address of the copy. */ + +char *savestring (const char *ptr, size_t len); + +/* The strerror() function can return NULL for errno values that are + out of range. Provide a "safe" version that always returns a + printable string. */ + +extern char *safe_strerror (int); + +/* Return non-zero if the start of STRING matches PATTERN, zero + otherwise. */ + +static inline int +startswith (const char *string, const char *pattern) +{ + return strncmp (string, pattern, strlen (pattern)) == 0; +} + +ULONGEST strtoulst (const char *num, const char **trailer, int base); + +/* Skip leading whitespace characters in INP, returning an updated + pointer. If INP is NULL, return NULL. */ + +extern char *skip_spaces (char *inp); + +/* A const-correct version of the above. */ + +extern const char *skip_spaces (const char *inp); + +/* Skip leading non-whitespace characters in INP, returning an updated + pointer. If INP is NULL, return NULL. */ + +extern char *skip_to_space (char *inp); + +/* A const-correct version of the above. */ + +extern const char *skip_to_space (const char *inp); + +/* Assumes that V is an argv for a program, and iterates through + freeing all the elements. */ +extern void free_vector_argv (std::vector<char *> &v); + +/* Given a vector of arguments ARGV, return a string equivalent to + joining all the arguments with a whitespace separating them. */ +extern std::string stringify_argv (const std::vector<char *> &argv); + +/* Return true if VALUE is in [LOW, HIGH]. */ + +template <typename T> +static bool +in_inclusive_range (T value, T low, T high) +{ + return value >= low && value <= high; +} + +/* Ensure that V is aligned to an N byte boundary (B's assumed to be a + power of 2). Round up/down when necessary. Examples of correct + use include: + + addr = align_up (addr, 8); -- VALUE needs 8 byte alignment + write_memory (addr, value, len); + addr += len; + + and: + + sp = align_down (sp - len, 16); -- Keep SP 16 byte aligned + write_memory (sp, value, len); + + Note that uses such as: + + write_memory (addr, value, len); + addr += align_up (len, 8); + + and: + + sp -= align_up (len, 8); + write_memory (sp, value, len); + + are typically not correct as they don't ensure that the address (SP + or ADDR) is correctly aligned (relying on previous alignment to + keep things right). This is also why the methods are called + "align_..." instead of "round_..." as the latter reads better with + this incorrect coding style. */ + +extern ULONGEST align_up (ULONGEST v, int n); +extern ULONGEST align_down (ULONGEST v, int n); + +#endif /* COMMON_COMMON_UTILS_H */ diff --git a/gdb/gdbsupport/common.host b/gdb/gdbsupport/common.host new file mode 100644 index 00000000000..4839059e610 --- /dev/null +++ b/gdb/gdbsupport/common.host @@ -0,0 +1,36 @@ +# Common object files to include for each host. +# +# Copyright (C) 2015-2019 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/>. + +# Map host triplet into the common object files to be included by +# GDB/gdbserver. This is invoked from the autoconf generated +# configure script. + +# This file sets the following shell variables: +# common_host_obs host-specific .o files to include when building +# GDB/gdbserver + +case "${host}" in + +*-mingw*) common_host_obs=gdbsupport/mingw-strerror.o + ;; +*) + common_host_obs=gdbsupport/posix-strerror.o + ;; + +esac diff --git a/gdb/gdbsupport/common.m4 b/gdb/gdbsupport/common.m4 new file mode 100644 index 00000000000..5701dd98293 --- /dev/null +++ b/gdb/gdbsupport/common.m4 @@ -0,0 +1,48 @@ +dnl Autoconf configure snippets for common. +dnl Copyright (C) 1995-2019 Free Software Foundation, Inc. +dnl +dnl This file is part of GDB. +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program. If not, see <http://www.gnu.org/licenses/>. + +dnl Invoke configury needed by the files in 'common'. +AC_DEFUN([GDB_AC_COMMON], [ + AC_HEADER_STDC + AC_FUNC_ALLOCA + + dnl Note that this requires codeset.m4, which is included + dnl by the users of common.m4. + AM_LANGINFO_CODESET + + AC_CHECK_HEADERS(linux/perf_event.h locale.h memory.h signal.h dnl + sys/resource.h sys/socket.h dnl + sys/un.h sys/wait.h dnl + thread_db.h wait.h dnl + termios.h) + + AC_CHECK_FUNCS([fdwalk getrlimit pipe pipe2 socketpair sigaction]) + + AC_CHECK_DECLS([strerror, strstr]) + + dnl Check if sigsetjmp is available. Using AC_CHECK_FUNCS won't + dnl do since sigsetjmp might only be defined as a macro. +AC_CACHE_CHECK([for sigsetjmp], gdb_cv_func_sigsetjmp, +[AC_TRY_COMPILE([ +#include <setjmp.h> +], [sigjmp_buf env; while (! sigsetjmp (env, 1)) siglongjmp (env, 1);], +gdb_cv_func_sigsetjmp=yes, gdb_cv_func_sigsetjmp=no)]) +if test $gdb_cv_func_sigsetjmp = yes; then + AC_DEFINE(HAVE_SIGSETJMP, 1, [Define if sigsetjmp is available. ]) +fi +]) diff --git a/gdb/gdbsupport/create-version.sh b/gdb/gdbsupport/create-version.sh new file mode 100755 index 00000000000..30525babb5a --- /dev/null +++ b/gdb/gdbsupport/create-version.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Copyright (C) 1989-2019 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/>. + +# Create version.c from version.in. +# Usage: +# create-version.sh PATH-TO-GDB-SRCDIR HOST_ALIAS \ +# TARGET_ALIAS OUTPUT-FILE-NAME + +srcdir="$1" +host_alias="$2" +target_alias="$3" +output="$4" + +rm -f version.c-tmp $output version.tmp +date=`sed -n -e 's/^.* BFD_VERSION_DATE \(.*\)$/\1/p' $srcdir/../bfd/version.h` +sed -e "s/DATE/$date/" < $srcdir/version.in > version.tmp +echo '#include "gdbsupport/version.h"' >> version.c-tmp +echo 'const char version[] = "'"`sed q version.tmp`"'";' >> version.c-tmp +echo 'const char host_name[] = "'"$host_alias"'";' >> version.c-tmp +echo 'const char target_name[] = "'"$target_alias"'";' >> version.c-tmp +mv version.c-tmp $output +rm -f version.tmp diff --git a/gdb/gdbsupport/def-vector.h b/gdb/gdbsupport/def-vector.h new file mode 100644 index 00000000000..fb933b8308d --- /dev/null +++ b/gdb/gdbsupport/def-vector.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_DEF_VECTOR_H +#define COMMON_DEF_VECTOR_H + +#include <vector> +#include "gdbsupport/default-init-alloc.h" + +namespace gdb { + +/* A vector that uses an allocator that default constructs using + default-initialization rather than value-initialization. The idea + is to use this when you don't want zero-initialization of elements + of vectors of trivial types. E.g., byte buffers. */ + +template<typename T> using def_vector + = std::vector<T, gdb::default_init_allocator<T>>; + +} /* namespace gdb */ + +#endif /* COMMON_DEF_VECTOR_H */ diff --git a/gdb/gdbsupport/default-init-alloc.h b/gdb/gdbsupport/default-init-alloc.h new file mode 100644 index 00000000000..2d739c9a635 --- /dev/null +++ b/gdb/gdbsupport/default-init-alloc.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_DEFAULT_INIT_ALLOC_H +#define COMMON_DEFAULT_INIT_ALLOC_H + +namespace gdb { + +/* An allocator that default constructs using default-initialization + rather than value-initialization. The idea is to use this when you + don't want to default construct elements of containers of trivial + types using zero-initialization. */ + +/* Mostly as implementation convenience, this is implemented as an + adapter that given an allocator A, overrides 'A::construct()'. 'A' + defaults to std::allocator<T>. */ + +template<typename T, typename A = std::allocator<T>> +class default_init_allocator : public A +{ +public: + /* Pull in A's ctors. */ + using A::A; + + /* Override rebind. */ + template<typename U> + struct rebind + { + /* A couple helpers just to make it a bit more readable. */ + typedef std::allocator_traits<A> traits_; + typedef typename traits_::template rebind_alloc<U> alloc_; + + /* This is what we're after. */ + typedef default_init_allocator<U, alloc_> other; + }; + + /* Make the base allocator's construct method(s) visible. */ + using A::construct; + + /* .. and provide an override/overload for the case of default + construction (i.e., no arguments). This is where we construct + with default-init. */ + template <typename U> + void construct (U *ptr) + noexcept (std::is_nothrow_default_constructible<U>::value) + { + ::new ((void *) ptr) U; /* default-init */ + } +}; + +} /* namespace gdb */ + +#endif /* COMMON_DEFAULT_INIT_ALLOC_H */ diff --git a/gdb/gdbsupport/enum-flags.h b/gdb/gdbsupport/enum-flags.h new file mode 100644 index 00000000000..88ba5910f9d --- /dev/null +++ b/gdb/gdbsupport/enum-flags.h @@ -0,0 +1,221 @@ +/* Copyright (C) 2015-2019 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/>. */ + +#ifndef COMMON_ENUM_FLAGS_H +#define COMMON_ENUM_FLAGS_H + +/* Type-safe wrapper for enum flags. enum flags are enums where the + values are bits that are meant to be ORed together. + + This allows writing code like the below, while with raw enums this + would fail to compile without casts to enum type at the assignments + to 'f': + + enum some_flag + { + flag_val1 = 1 << 1, + flag_val2 = 1 << 2, + flag_val3 = 1 << 3, + flag_val4 = 1 << 4, + }; + DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags); + + some_flags f = flag_val1 | flag_val2; + f |= flag_val3; + + It's also possible to assign literal zero to an enum flags variable + (meaning, no flags), dispensing adding an awkward explicit "no + value" value to the enumeration. For example: + + some_flags f = 0; + f |= flag_val3 | flag_val4; + + Note that literal integers other than zero fail to compile: + + some_flags f = 1; // error +*/ + +#ifdef __cplusplus + +/* Traits type used to prevent the global operator overloads from + instantiating for non-flag enums. */ +template<typename T> struct enum_flags_type {}; + +/* Use this to mark an enum as flags enum. It defines FLAGS as + enum_flags wrapper class for ENUM, and enables the global operator + overloads for ENUM. */ +#define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \ + typedef enum_flags<enum_type> flags_type; \ + template<> \ + struct enum_flags_type<enum_type> \ + { \ + typedef enum_flags<enum_type> type; \ + } + +/* Until we can rely on std::underlying type being universally + available (C++11), roll our own for enums. */ +template<int size, bool sign> class integer_for_size { typedef void type; }; +template<> struct integer_for_size<1, 0> { typedef uint8_t type; }; +template<> struct integer_for_size<2, 0> { typedef uint16_t type; }; +template<> struct integer_for_size<4, 0> { typedef uint32_t type; }; +template<> struct integer_for_size<8, 0> { typedef uint64_t type; }; +template<> struct integer_for_size<1, 1> { typedef int8_t type; }; +template<> struct integer_for_size<2, 1> { typedef int16_t type; }; +template<> struct integer_for_size<4, 1> { typedef int32_t type; }; +template<> struct integer_for_size<8, 1> { typedef int64_t type; }; + +template<typename T> +struct enum_underlying_type +{ + typedef typename + integer_for_size<sizeof (T), static_cast<bool>(T (-1) < T (0))>::type + type; +}; + +template <typename E> +class enum_flags +{ +public: + typedef E enum_type; + typedef typename enum_underlying_type<enum_type>::type underlying_type; + +private: + /* Private type used to support initializing flag types with zero: + + foo_flags f = 0; + + but not other integers: + + foo_flags f = 1; + + The way this works is that we define an implicit constructor that + takes a pointer to this private type. Since nothing can + instantiate an object of this type, the only possible pointer to + pass to the constructor is the NULL pointer, or, zero. */ + struct zero_type; + + underlying_type + underlying_value () const + { + return m_enum_value; + } + +public: + /* Allow default construction. */ + enum_flags () + : m_enum_value ((enum_type) 0) + {} + + /* If you get an error saying these two overloads are ambiguous, + then you tried to mix values of different enum types. */ + enum_flags (enum_type e) + : m_enum_value (e) + {} + enum_flags (struct enum_flags::zero_type *zero) + : m_enum_value ((enum_type) 0) + {} + + enum_flags &operator&= (enum_type e) + { + m_enum_value = (enum_type) (underlying_value () & e); + return *this; + } + enum_flags &operator|= (enum_type e) + { + m_enum_value = (enum_type) (underlying_value () | e); + return *this; + } + enum_flags &operator^= (enum_type e) + { + m_enum_value = (enum_type) (underlying_value () ^ e); + return *this; + } + + operator enum_type () const + { + return m_enum_value; + } + + enum_flags operator& (enum_type e) const + { + return (enum_type) (underlying_value () & e); + } + enum_flags operator| (enum_type e) const + { + return (enum_type) (underlying_value () | e); + } + enum_flags operator^ (enum_type e) const + { + return (enum_type) (underlying_value () ^ e); + } + enum_flags operator~ () const + { + // We only the underlying type to be unsigned when actually using + // operator~ -- if it were not unsigned, undefined behavior could + // result. However, asserting this in the class itself would + // require too many unnecessary changes to otherwise ok enum + // types. + gdb_static_assert (std::is_unsigned<underlying_type>::value); + return (enum_type) ~underlying_value (); + } + +private: + /* Stored as enum_type because GDB knows to print the bit flags + neatly if the enum values look like bit flags. */ + enum_type m_enum_value; +}; + +/* Global operator overloads. */ + +template <typename enum_type> +typename enum_flags_type<enum_type>::type +operator& (enum_type e1, enum_type e2) +{ + return enum_flags<enum_type> (e1) & e2; +} + +template <typename enum_type> +typename enum_flags_type<enum_type>::type +operator| (enum_type e1, enum_type e2) +{ + return enum_flags<enum_type> (e1) | e2; +} + +template <typename enum_type> +typename enum_flags_type<enum_type>::type +operator^ (enum_type e1, enum_type e2) +{ + return enum_flags<enum_type> (e1) ^ e2; +} + +template <typename enum_type> +typename enum_flags_type<enum_type>::type +operator~ (enum_type e) +{ + return ~enum_flags<enum_type> (e); +} + +#else /* __cplusplus */ + +/* In C, the flags type is just a typedef for the enum type. */ + +#define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \ + typedef enum_type flags_type + +#endif /* __cplusplus */ + +#endif /* COMMON_ENUM_FLAGS_H */ diff --git a/gdb/gdbsupport/environ.c b/gdb/gdbsupport/environ.c new file mode 100644 index 00000000000..006d80afdce --- /dev/null +++ b/gdb/gdbsupport/environ.c @@ -0,0 +1,183 @@ +/* environ.c -- library for manipulating environments for GNU. + + Copyright (C) 1986-2019 Free Software Foundation, Inc. + + 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 "environ.h" +#include <algorithm> +#include <utility> + +/* See gdbsupport/environ.h. */ + +gdb_environ & +gdb_environ::operator= (gdb_environ &&e) +{ + /* Are we self-moving? */ + if (&e == this) + return *this; + + m_environ_vector = std::move (e.m_environ_vector); + m_user_set_env = std::move (e.m_user_set_env); + m_user_unset_env = std::move (e.m_user_unset_env); + e.m_environ_vector.clear (); + e.m_environ_vector.push_back (NULL); + e.m_user_set_env.clear (); + e.m_user_unset_env.clear (); + return *this; +} + +/* See gdbsupport/environ.h. */ + +gdb_environ gdb_environ::from_host_environ () +{ + extern char **environ; + gdb_environ e; + + if (environ == NULL) + return e; + + for (int i = 0; environ[i] != NULL; ++i) + { + /* Make sure we add the element before the last (NULL). */ + e.m_environ_vector.insert (e.m_environ_vector.end () - 1, + xstrdup (environ[i])); + } + + return e; +} + +/* See gdbsupport/environ.h. */ + +void +gdb_environ::clear () +{ + for (char *v : m_environ_vector) + xfree (v); + m_environ_vector.clear (); + /* Always add the NULL element. */ + m_environ_vector.push_back (NULL); + m_user_set_env.clear (); + m_user_unset_env.clear (); +} + +/* Helper function to check if STRING contains an environment variable + assignment of VAR, i.e., if STRING starts with 'VAR='. Return true + if it contains, false otherwise. */ + +static bool +match_var_in_string (const char *string, const char *var, size_t var_len) +{ + if (strncmp (string, var, var_len) == 0 && string[var_len] == '=') + return true; + + return false; +} + +/* See gdbsupport/environ.h. */ + +const char * +gdb_environ::get (const char *var) const +{ + size_t len = strlen (var); + + for (char *el : m_environ_vector) + if (el != NULL && match_var_in_string (el, var, len)) + return &el[len + 1]; + + return NULL; +} + +/* See gdbsupport/environ.h. */ + +void +gdb_environ::set (const char *var, const char *value) +{ + char *fullvar = concat (var, "=", value, NULL); + + /* We have to unset the variable in the vector if it exists. */ + unset (var, false); + + /* Insert the element before the last one, which is always NULL. */ + m_environ_vector.insert (m_environ_vector.end () - 1, fullvar); + + /* Mark this environment variable as having been set by the user. + This will be useful when we deal with setting environment + variables on the remote target. */ + m_user_set_env.insert (std::string (fullvar)); + + /* If this environment variable is marked as unset by the user, then + remove it from the list, because now the user wants to set + it. */ + m_user_unset_env.erase (std::string (var)); +} + +/* See gdbsupport/environ.h. */ + +void +gdb_environ::unset (const char *var, bool update_unset_list) +{ + size_t len = strlen (var); + std::vector<char *>::iterator it_env; + + /* We iterate until '.end () - 1' because the last element is + always NULL. */ + for (it_env = m_environ_vector.begin (); + it_env != m_environ_vector.end () - 1; + ++it_env) + if (match_var_in_string (*it_env, var, len)) + break; + + if (it_env != m_environ_vector.end () - 1) + { + m_user_set_env.erase (std::string (*it_env)); + xfree (*it_env); + + m_environ_vector.erase (it_env); + } + + if (update_unset_list) + m_user_unset_env.insert (std::string (var)); +} + +/* See gdbsupport/environ.h. */ + +void +gdb_environ::unset (const char *var) +{ + unset (var, true); +} + +/* See gdbsupport/environ.h. */ + +char ** +gdb_environ::envp () const +{ + return const_cast<char **> (&m_environ_vector[0]); +} + +/* See gdbsupport/environ.h. */ + +const std::set<std::string> & +gdb_environ::user_set_env () const +{ + return m_user_set_env; +} + +const std::set<std::string> & +gdb_environ::user_unset_env () const +{ + return m_user_unset_env; +} diff --git a/gdb/gdbsupport/environ.h b/gdb/gdbsupport/environ.h new file mode 100644 index 00000000000..8a6b907c9b4 --- /dev/null +++ b/gdb/gdbsupport/environ.h @@ -0,0 +1,103 @@ +/* Header for environment manipulation library. + Copyright (C) 1989-2019 Free Software Foundation, Inc. + + 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/>. */ + +#ifndef COMMON_ENVIRON_H +#define COMMON_ENVIRON_H + +#include <vector> +#include <set> + +/* Class that represents the environment variables as seen by the + inferior. */ + +class gdb_environ +{ +public: + /* Regular constructor and destructor. */ + gdb_environ () + { + /* Make sure that the vector contains at least a NULL element. + If/when we add more variables to it, NULL will always be the + last element. */ + m_environ_vector.push_back (NULL); + } + + ~gdb_environ () + { + clear (); + } + + /* Move constructor. */ + gdb_environ (gdb_environ &&e) + : m_environ_vector (std::move (e.m_environ_vector)), + m_user_set_env (std::move (e.m_user_set_env)), + m_user_unset_env (std::move (e.m_user_unset_env)) + { + /* Make sure that the moved-from vector is left at a valid + state (only one NULL element). */ + e.m_environ_vector.clear (); + e.m_environ_vector.push_back (NULL); + e.m_user_set_env.clear (); + e.m_user_unset_env.clear (); + } + + /* Move assignment. */ + gdb_environ &operator= (gdb_environ &&e); + + /* Create a gdb_environ object using the host's environment + variables. */ + static gdb_environ from_host_environ (); + + /* Clear the environment variables stored in the object. */ + void clear (); + + /* Return the value in the environment for the variable VAR. The + returned pointer is only valid as long as the gdb_environ object + is not modified. */ + const char *get (const char *var) const; + + /* Store VAR=VALUE in the environment. */ + void set (const char *var, const char *value); + + /* Unset VAR in environment. */ + void unset (const char *var); + + /* Return the environment vector represented as a 'char **'. */ + char **envp () const; + + /* Return the user-set environment vector. */ + const std::set<std::string> &user_set_env () const; + + /* Return the user-unset environment vector. */ + const std::set<std::string> &user_unset_env () const; + +private: + /* Unset VAR in environment. If UPDATE_UNSET_LIST is true, then + also update M_USER_UNSET_ENV to reflect the unsetting of the + environment variable. */ + void unset (const char *var, bool update_unset_list); + + /* A vector containing the environment variables. */ + std::vector<char *> m_environ_vector; + + /* The environment variables explicitly set by the user. */ + std::set<std::string> m_user_set_env; + + /* The environment variables explicitly unset by the user. */ + std::set<std::string> m_user_unset_env; +}; + +#endif /* COMMON_ENVIRON_H */ diff --git a/gdb/gdbsupport/errors.c b/gdb/gdbsupport/errors.c new file mode 100644 index 00000000000..96be0381850 --- /dev/null +++ b/gdb/gdbsupport/errors.c @@ -0,0 +1,69 @@ +/* Error reporting facilities. + + Copyright (C) 1986-2019 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 "errors.h" + +/* See gdbsupport/errors.h. */ + +void +warning (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vwarning (fmt, ap); + va_end (ap); +} + +/* See gdbsupport/errors.h. */ + +void +error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + verror (fmt, ap); + va_end (ap); +} + +/* See gdbsupport/errors.h. */ + +void +internal_error (const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + internal_verror (file, line, fmt, ap); + va_end (ap); +} + +/* See gdbsupport/errors.h. */ + +void +internal_warning (const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + internal_vwarning (file, line, fmt, ap); + va_end (ap); +} diff --git a/gdb/gdbsupport/errors.h b/gdb/gdbsupport/errors.h new file mode 100644 index 00000000000..8dbc6cff5f6 --- /dev/null +++ b/gdb/gdbsupport/errors.h @@ -0,0 +1,90 @@ +/* Declarations for error-reporting facilities. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_ERRORS_H +#define COMMON_ERRORS_H + +/* A problem was detected, but the requested operation can still + proceed. A warning message is constructed using a printf- or + vprintf-style argument list. The function "vwarning" must be + provided by the client. */ + +extern void warning (const char *fmt, ...) + ATTRIBUTE_PRINTF (1, 2); + +extern void vwarning (const char *fmt, va_list args) + ATTRIBUTE_PRINTF (1, 0); + +/* A non-predictable, non-fatal error was detected. The requested + operation cannot proceed. An error message is constructed using + a printf- or vprintf-style argument list. These functions do not + return. The function "verror" must be provided by the client. */ + +extern void error (const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2); + +extern void verror (const char *fmt, va_list args) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0); + +/* An internal error was detected. Internal errors indicate + programming errors such as assertion failures, as opposed to + more general errors beyond the application's control. These + functions do not return. An error message is constructed using + a printf- or vprintf-style argument list. FILE and LINE + indicate the file and line number where the programming error + was detected. The function "internal_verror" must be provided + by the client. */ + +extern void internal_error (const char *file, int line, + const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 4); + +extern void internal_verror (const char *file, int line, + const char *fmt, va_list args) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0); + +/* An internal problem was detected, but the requested operation can + still proceed. Internal warnings indicate programming errors as + opposed to more general issues beyond the application's control. + A warning message is constructed using a printf- or vprintf-style + argument list. The function "internal_vwarning" must be provided + by the client. */ + +extern void internal_warning (const char *file, int line, + const char *fmt, ...) + ATTRIBUTE_PRINTF (3, 4); + +extern void internal_vwarning (const char *file, int line, + const char *fmt, va_list args) + ATTRIBUTE_PRINTF (3, 0); + + +/* Like "error", but the error message is constructed by combining + STRING with the system error message for errno. This function does + not return. This function must be provided by the client. */ + +extern void perror_with_name (const char *string) ATTRIBUTE_NORETURN; + +/* Call this function to handle memory allocation failures. This + function does not return. This function must be provided by the + client. */ + +extern void malloc_failure (long size) ATTRIBUTE_NORETURN; + +#endif /* COMMON_ERRORS_H */ diff --git a/gdb/gdbsupport/fileio.c b/gdb/gdbsupport/fileio.c new file mode 100644 index 00000000000..28d97fc2f26 --- /dev/null +++ b/gdb/gdbsupport/fileio.c @@ -0,0 +1,255 @@ +/* File-I/O functions for GDB, the GNU debugger. + + Copyright (C) 2003-2019 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 "fileio.h" +#include <sys/stat.h> +#include <fcntl.h> + +/* See fileio.h. */ + +int +host_to_fileio_error (int error) +{ + switch (error) + { + case EPERM: + return FILEIO_EPERM; + case ENOENT: + return FILEIO_ENOENT; + case EINTR: + return FILEIO_EINTR; + case EIO: + return FILEIO_EIO; + case EBADF: + return FILEIO_EBADF; + case EACCES: + return FILEIO_EACCES; + case EFAULT: + return FILEIO_EFAULT; + case EBUSY: + return FILEIO_EBUSY; + case EEXIST: + return FILEIO_EEXIST; + case ENODEV: + return FILEIO_ENODEV; + case ENOTDIR: + return FILEIO_ENOTDIR; + case EISDIR: + return FILEIO_EISDIR; + case EINVAL: + return FILEIO_EINVAL; + case ENFILE: + return FILEIO_ENFILE; + case EMFILE: + return FILEIO_EMFILE; + case EFBIG: + return FILEIO_EFBIG; + case ENOSPC: + return FILEIO_ENOSPC; + case ESPIPE: + return FILEIO_ESPIPE; + case EROFS: + return FILEIO_EROFS; + case ENOSYS: + return FILEIO_ENOSYS; + case ENAMETOOLONG: + return FILEIO_ENAMETOOLONG; + } + return FILEIO_EUNKNOWN; +} + +/* See fileio.h. */ + +int +fileio_to_host_openflags (int fileio_open_flags, int *open_flags_p) +{ + int open_flags = 0; + + if (fileio_open_flags & ~FILEIO_O_SUPPORTED) + return -1; + + if (fileio_open_flags & FILEIO_O_CREAT) + open_flags |= O_CREAT; + if (fileio_open_flags & FILEIO_O_EXCL) + open_flags |= O_EXCL; + if (fileio_open_flags & FILEIO_O_TRUNC) + open_flags |= O_TRUNC; + if (fileio_open_flags & FILEIO_O_APPEND) + open_flags |= O_APPEND; + if (fileio_open_flags & FILEIO_O_RDONLY) + open_flags |= O_RDONLY; + if (fileio_open_flags & FILEIO_O_WRONLY) + open_flags |= O_WRONLY; + if (fileio_open_flags & FILEIO_O_RDWR) + open_flags |= O_RDWR; + /* On systems supporting binary and text mode, always open files + in binary mode. */ +#ifdef O_BINARY + open_flags |= O_BINARY; +#endif + + *open_flags_p = open_flags; + return 0; +} + +/* See fileio.h. */ + +int +fileio_to_host_mode (int fileio_mode, mode_t *mode_p) +{ + mode_t mode = 0; + + if (fileio_mode & ~FILEIO_S_SUPPORTED) + return -1; + + if (fileio_mode & FILEIO_S_IFREG) + mode |= S_IFREG; + if (fileio_mode & FILEIO_S_IFDIR) + mode |= S_IFDIR; + if (fileio_mode & FILEIO_S_IFCHR) + mode |= S_IFCHR; + if (fileio_mode & FILEIO_S_IRUSR) + mode |= S_IRUSR; + if (fileio_mode & FILEIO_S_IWUSR) + mode |= S_IWUSR; + if (fileio_mode & FILEIO_S_IXUSR) + mode |= S_IXUSR; +#ifdef S_IRGRP + if (fileio_mode & FILEIO_S_IRGRP) + mode |= S_IRGRP; +#endif +#ifdef S_IWGRP + if (fileio_mode & FILEIO_S_IWGRP) + mode |= S_IWGRP; +#endif +#ifdef S_IXGRP + if (fileio_mode & FILEIO_S_IXGRP) + mode |= S_IXGRP; +#endif + if (fileio_mode & FILEIO_S_IROTH) + mode |= S_IROTH; +#ifdef S_IWOTH + if (fileio_mode & FILEIO_S_IWOTH) + mode |= S_IWOTH; +#endif +#ifdef S_IXOTH + if (fileio_mode & FILEIO_S_IXOTH) + mode |= S_IXOTH; +#endif + + *mode_p = mode; + return 0; +} + +/* Convert a host-format mode_t into a bitmask of File-I/O flags. */ + +static LONGEST +fileio_mode_pack (mode_t mode) +{ + mode_t tmode = 0; + + if (S_ISREG (mode)) + tmode |= FILEIO_S_IFREG; + if (S_ISDIR (mode)) + tmode |= FILEIO_S_IFDIR; + if (S_ISCHR (mode)) + tmode |= FILEIO_S_IFCHR; + if (mode & S_IRUSR) + tmode |= FILEIO_S_IRUSR; + if (mode & S_IWUSR) + tmode |= FILEIO_S_IWUSR; + if (mode & S_IXUSR) + tmode |= FILEIO_S_IXUSR; +#ifdef S_IRGRP + if (mode & S_IRGRP) + tmode |= FILEIO_S_IRGRP; +#endif +#ifdef S_IWGRP + if (mode & S_IWGRP) + tmode |= FILEIO_S_IWGRP; +#endif +#ifdef S_IXGRP + if (mode & S_IXGRP) + tmode |= FILEIO_S_IXGRP; +#endif + if (mode & S_IROTH) + tmode |= FILEIO_S_IROTH; +#ifdef S_IWOTH + if (mode & S_IWOTH) + tmode |= FILEIO_S_IWOTH; +#endif +#ifdef S_IXOTH + if (mode & S_IXOTH) + tmode |= FILEIO_S_IXOTH; +#endif + return tmode; +} + +/* Pack a host-format mode_t into an fio_mode_t. */ + +static void +host_to_fileio_mode (mode_t num, fio_mode_t fnum) +{ + host_to_bigendian (fileio_mode_pack (num), (char *) fnum, 4); +} + +/* Pack a host-format integer into an fio_ulong_t. */ + +static void +host_to_fileio_ulong (LONGEST num, fio_ulong_t fnum) +{ + host_to_bigendian (num, (char *) fnum, 8); +} + +/* See fileio.h. */ + +void +host_to_fileio_stat (struct stat *st, struct fio_stat *fst) +{ + LONGEST blksize; + + host_to_fileio_uint ((long) st->st_dev, fst->fst_dev); + host_to_fileio_uint ((long) st->st_ino, fst->fst_ino); + host_to_fileio_mode (st->st_mode, fst->fst_mode); + host_to_fileio_uint ((long) st->st_nlink, fst->fst_nlink); + host_to_fileio_uint ((long) st->st_uid, fst->fst_uid); + host_to_fileio_uint ((long) st->st_gid, fst->fst_gid); + host_to_fileio_uint ((long) st->st_rdev, fst->fst_rdev); + host_to_fileio_ulong ((LONGEST) st->st_size, fst->fst_size); +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + blksize = st->st_blksize; +#else + blksize = 512; +#endif + host_to_fileio_ulong (blksize, fst->fst_blksize); +#if HAVE_STRUCT_STAT_ST_BLOCKS + host_to_fileio_ulong ((LONGEST) st->st_blocks, fst->fst_blocks); +#else + /* FIXME: This is correct for DJGPP, but other systems that don't + have st_blocks, if any, might prefer 512 instead of st_blksize. + (eliz, 30-12-2003) */ + host_to_fileio_ulong (((LONGEST) st->st_size + blksize - 1) + / blksize, + fst->fst_blocks); +#endif + host_to_fileio_time (st->st_atime, fst->fst_atime); + host_to_fileio_time (st->st_mtime, fst->fst_mtime); + host_to_fileio_time (st->st_ctime, fst->fst_ctime); +} diff --git a/gdb/gdbsupport/fileio.h b/gdb/gdbsupport/fileio.h new file mode 100644 index 00000000000..7a2e7c2817e --- /dev/null +++ b/gdb/gdbsupport/fileio.h @@ -0,0 +1,73 @@ +/* File-I/O functions for GDB, the GNU debugger. + + Copyright (C) 2003-2019 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/>. */ + +#ifndef COMMON_FILEIO_H +#define COMMON_FILEIO_H + +#include "gdb/fileio.h" +#include <sys/stat.h> + +/* Convert a host-format errno value to a File-I/O error number. */ + +extern int host_to_fileio_error (int error); + +/* Convert File-I/O open flags FFLAGS to host format, storing + the result in *FLAGS. Return 0 on success, -1 on error. */ + +extern int fileio_to_host_openflags (int fflags, int *flags); + +/* Convert File-I/O mode FMODE to host format, storing + the result in *MODE. Return 0 on success, -1 on error. */ + +extern int fileio_to_host_mode (int fmode, mode_t *mode); + +/* Pack a host-format integer into a byte buffer in big-endian + format. BYTES specifies the size of the integer to pack in + bytes. */ + +static inline void +host_to_bigendian (LONGEST num, char *buf, int bytes) +{ + int i; + + for (i = 0; i < bytes; ++i) + buf[i] = (num >> (8 * (bytes - i - 1))) & 0xff; +} + +/* Pack a host-format integer into an fio_uint_t. */ + +static inline void +host_to_fileio_uint (long num, fio_uint_t fnum) +{ + host_to_bigendian ((LONGEST) num, (char *) fnum, 4); +} + +/* Pack a host-format time_t into an fio_time_t. */ + +static inline void +host_to_fileio_time (time_t num, fio_time_t fnum) +{ + host_to_bigendian ((LONGEST) num, (char *) fnum, 4); +} + +/* Pack a host-format struct stat into a struct fio_stat. */ + +extern void host_to_fileio_stat (struct stat *st, struct fio_stat *fst); + +#endif /* COMMON_FILEIO_H */ diff --git a/gdb/gdbsupport/filestuff.c b/gdb/gdbsupport/filestuff.c new file mode 100644 index 00000000000..b8fca1552a4 --- /dev/null +++ b/gdb/gdbsupport/filestuff.c @@ -0,0 +1,503 @@ +/* Low-level file-handling. + Copyright (C) 2012-2019 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 "filestuff.h" +#include "gdb_vecs.h" +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <algorithm> + +#ifdef USE_WIN32API +#include <winsock2.h> +#include <windows.h> +#define HAVE_SOCKETS 1 +#elif defined HAVE_SYS_SOCKET_H +#include <sys/socket.h> +/* Define HAVE_F_GETFD if we plan to use F_GETFD. */ +#define HAVE_F_GETFD F_GETFD +#define HAVE_SOCKETS 1 +#endif + +#ifdef HAVE_KINFO_GETFILE +#include <sys/user.h> +#include <libutil.h> +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif /* HAVE_SYS_RESOURCE_H */ + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +#ifndef O_NOINHERIT +#define O_NOINHERIT 0 +#endif + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + + + +#ifndef HAVE_FDWALK + +#include <dirent.h> + +/* Replacement for fdwalk, if the system doesn't define it. Walks all + open file descriptors (though this implementation may walk closed + ones as well, depending on the host platform's capabilities) and + call FUNC with ARG. If FUNC returns non-zero, stops immediately + and returns the same value. Otherwise, returns zero when + finished. */ + +static int +fdwalk (int (*func) (void *, int), void *arg) +{ + /* Checking __linux__ isn't great but it isn't clear what would be + better. There doesn't seem to be a good way to check for this in + configure. */ +#ifdef __linux__ + DIR *dir; + + dir = opendir ("/proc/self/fd"); + if (dir != NULL) + { + struct dirent *entry; + int result = 0; + + for (entry = readdir (dir); entry != NULL; entry = readdir (dir)) + { + long fd; + char *tail; + + errno = 0; + fd = strtol (entry->d_name, &tail, 10); + if (*tail != '\0' || errno != 0) + continue; + if ((int) fd != fd) + { + /* What can we do here really? */ + continue; + } + + if (fd == dirfd (dir)) + continue; + + result = func (arg, fd); + if (result != 0) + break; + } + + closedir (dir); + return result; + } + /* We may fall through to the next case. */ +#endif +#ifdef HAVE_KINFO_GETFILE + int nfd; + gdb::unique_xmalloc_ptr<struct kinfo_file[]> fdtbl + (kinfo_getfile (getpid (), &nfd)); + if (fdtbl != NULL) + { + for (int i = 0; i < nfd; i++) + { + if (fdtbl[i].kf_fd >= 0) + { + int result = func (arg, fdtbl[i].kf_fd); + if (result != 0) + return result; + } + } + return 0; + } + /* We may fall through to the next case. */ +#endif + + { + int max, fd; + +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rlim; + + if (getrlimit (RLIMIT_NOFILE, &rlim) == 0 && rlim.rlim_max != RLIM_INFINITY) + max = rlim.rlim_max; + else +#endif + { +#ifdef _SC_OPEN_MAX + max = sysconf (_SC_OPEN_MAX); +#else + /* Whoops. */ + return 0; +#endif /* _SC_OPEN_MAX */ + } + + for (fd = 0; fd < max; ++fd) + { + struct stat sb; + int result; + + /* Only call FUNC for open fds. */ + if (fstat (fd, &sb) == -1) + continue; + + result = func (arg, fd); + if (result != 0) + return result; + } + + return 0; + } +} + +#endif /* HAVE_FDWALK */ + + + +/* A vector holding all the fds open when notice_open_fds was called. We + don't use a hashtab because we don't expect there to be many open fds. */ + +static std::vector<int> open_fds; + +/* An fdwalk callback function used by notice_open_fds. It puts the + given file descriptor into the vec. */ + +static int +do_mark_open_fd (void *ignore, int fd) +{ + open_fds.push_back (fd); + return 0; +} + +/* See filestuff.h. */ + +void +notice_open_fds (void) +{ + fdwalk (do_mark_open_fd, NULL); +} + +/* See filestuff.h. */ + +void +mark_fd_no_cloexec (int fd) +{ + do_mark_open_fd (NULL, fd); +} + +/* See filestuff.h. */ + +void +unmark_fd_no_cloexec (int fd) +{ + auto it = std::remove (open_fds.begin (), open_fds.end (), fd); + + if (it != open_fds.end ()) + open_fds.erase (it); + else + gdb_assert_not_reached (_("fd not found in open_fds")); +} + +/* Helper function for close_most_fds that closes the file descriptor + if appropriate. */ + +static int +do_close (void *ignore, int fd) +{ + for (int val : open_fds) + { + if (fd == val) + { + /* Keep this one open. */ + return 0; + } + } + + close (fd); + return 0; +} + +/* See filestuff.h. */ + +void +close_most_fds (void) +{ + fdwalk (do_close, NULL); +} + + + +/* This is a tri-state flag. When zero it means we haven't yet tried + O_CLOEXEC. When positive it means that O_CLOEXEC works on this + host. When negative, it means that O_CLOEXEC doesn't work. We + track this state because, while gdb might have been compiled + against a libc that supplies O_CLOEXEC, there is no guarantee that + the kernel supports it. */ + +static int trust_o_cloexec; + +/* Mark FD as close-on-exec, ignoring errors. Update + TRUST_O_CLOEXEC. */ + +static void +mark_cloexec (int fd) +{ +#ifdef HAVE_F_GETFD + int old = fcntl (fd, F_GETFD, 0); + + if (old != -1) + { + fcntl (fd, F_SETFD, old | FD_CLOEXEC); + + if (trust_o_cloexec == 0) + { + if ((old & FD_CLOEXEC) != 0) + trust_o_cloexec = 1; + else + trust_o_cloexec = -1; + } + } +#endif /* HAVE_F_GETFD */ +} + +/* Depending on TRUST_O_CLOEXEC, mark FD as close-on-exec. */ + +static void +maybe_mark_cloexec (int fd) +{ + if (trust_o_cloexec <= 0) + mark_cloexec (fd); +} + +#ifdef HAVE_SOCKETS + +/* Like maybe_mark_cloexec, but for callers that use SOCK_CLOEXEC. */ + +static void +socket_mark_cloexec (int fd) +{ + if (SOCK_CLOEXEC == 0 || trust_o_cloexec <= 0) + mark_cloexec (fd); +} + +#endif + + + +/* See filestuff.h. */ + +int +gdb_open_cloexec (const char *filename, int flags, unsigned long mode) +{ + int fd = open (filename, flags | O_CLOEXEC, mode); + + if (fd >= 0) + maybe_mark_cloexec (fd); + + return fd; +} + +/* See filestuff.h. */ + +gdb_file_up +gdb_fopen_cloexec (const char *filename, const char *opentype) +{ + FILE *result; + /* Probe for "e" support once. But, if we can tell the operating + system doesn't know about close on exec mode "e" without probing, + skip it. E.g., the Windows runtime issues an "Invalid parameter + passed to C runtime function" OutputDebugString warning for + unknown modes. Assume that if O_CLOEXEC is zero, then "e" isn't + supported. On MinGW, O_CLOEXEC is an alias of O_NOINHERIT, and + "e" isn't supported. */ + static int fopen_e_ever_failed_einval = + O_CLOEXEC == 0 || O_CLOEXEC == O_NOINHERIT; + + if (!fopen_e_ever_failed_einval) + { + char *copy; + + copy = (char *) alloca (strlen (opentype) + 2); + strcpy (copy, opentype); + /* This is a glibc extension but we try it unconditionally on + this path. */ + strcat (copy, "e"); + result = fopen (filename, copy); + + if (result == NULL && errno == EINVAL) + { + result = fopen (filename, opentype); + if (result != NULL) + fopen_e_ever_failed_einval = 1; + } + } + else + result = fopen (filename, opentype); + + if (result != NULL) + maybe_mark_cloexec (fileno (result)); + + return gdb_file_up (result); +} + +#ifdef HAVE_SOCKETS +/* See filestuff.h. */ + +int +gdb_socketpair_cloexec (int domain, int style, int protocol, + int filedes[2]) +{ +#ifdef HAVE_SOCKETPAIR + int result = socketpair (domain, style | SOCK_CLOEXEC, protocol, filedes); + + if (result != -1) + { + socket_mark_cloexec (filedes[0]); + socket_mark_cloexec (filedes[1]); + } + + return result; +#else + gdb_assert_not_reached (_("socketpair not available on this host")); +#endif +} + +/* See filestuff.h. */ + +int +gdb_socket_cloexec (int domain, int style, int protocol) +{ + int result = socket (domain, style | SOCK_CLOEXEC, protocol); + + if (result != -1) + socket_mark_cloexec (result); + + return result; +} +#endif + +/* See filestuff.h. */ + +int +gdb_pipe_cloexec (int filedes[2]) +{ + int result; + +#ifdef HAVE_PIPE2 + result = pipe2 (filedes, O_CLOEXEC); + if (result != -1) + { + maybe_mark_cloexec (filedes[0]); + maybe_mark_cloexec (filedes[1]); + } +#else +#ifdef HAVE_PIPE + result = pipe (filedes); + if (result != -1) + { + mark_cloexec (filedes[0]); + mark_cloexec (filedes[1]); + } +#else /* HAVE_PIPE */ + gdb_assert_not_reached (_("pipe not available on this host")); +#endif /* HAVE_PIPE */ +#endif /* HAVE_PIPE2 */ + + return result; +} + +/* See gdbsupport/filestuff.h. */ + +bool +is_regular_file (const char *name, int *errno_ptr) +{ + struct stat st; + const int status = stat (name, &st); + + /* Stat should never fail except when the file does not exist. + If stat fails, analyze the source of error and return true + unless the file does not exist, to avoid returning false results + on obscure systems where stat does not work as expected. */ + + if (status != 0) + { + if (errno != ENOENT) + return true; + *errno_ptr = ENOENT; + return false; + } + + if (S_ISREG (st.st_mode)) + return true; + + if (S_ISDIR (st.st_mode)) + *errno_ptr = EISDIR; + else + *errno_ptr = EINVAL; + return false; +} + +/* See gdbsupport/filestuff.h. */ + +bool +mkdir_recursive (const char *dir) +{ + auto holder = make_unique_xstrdup (dir); + char * const start = holder.get (); + char *component_start = start; + char *component_end = start; + + while (1) + { + /* Find the beginning of the next component. */ + while (*component_start == '/') + component_start++; + + /* Are we done? */ + if (*component_start == '\0') + return true; + + /* Find the slash or null-terminator after this component. */ + component_end = component_start; + while (*component_end != '/' && *component_end != '\0') + component_end++; + + /* Temporarily replace the slash with a null terminator, so we can create + the directory up to this component. */ + char saved_char = *component_end; + *component_end = '\0'; + + /* If we get EEXIST and the existing path is a directory, then we're + happy. If it exists, but it's a regular file and this is not the last + component, we'll fail at the next component. If this is the last + component, the caller will fail with ENOTDIR when trying to + open/create a file under that path. */ + if (mkdir (start, 0700) != 0) + if (errno != EEXIST) + return false; + + /* Restore the overwritten char. */ + *component_end = saved_char; + component_start = component_end; + } +} diff --git a/gdb/gdbsupport/filestuff.h b/gdb/gdbsupport/filestuff.h new file mode 100644 index 00000000000..c50781da01c --- /dev/null +++ b/gdb/gdbsupport/filestuff.h @@ -0,0 +1,142 @@ +/* Low-level file-handling. + Copyright (C) 2012-2019 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/>. */ + +#ifndef COMMON_FILESTUFF_H +#define COMMON_FILESTUFF_H + +#include <dirent.h> +#include <fcntl.h> + +/* Note all the file descriptors which are open when this is called. + These file descriptors will not be closed by close_most_fds. */ + +extern void notice_open_fds (void); + +/* Mark a file descriptor as inheritable across an exec. */ + +extern void mark_fd_no_cloexec (int fd); + +/* Mark a file descriptor as no longer being inheritable across an + exec. This is only meaningful when FD was previously passed to + mark_fd_no_cloexec. */ + +extern void unmark_fd_no_cloexec (int fd); + +/* Close all open file descriptors other than those marked by + 'notice_open_fds', and stdin, stdout, and stderr. Errors that + occur while closing are ignored. */ + +extern void close_most_fds (void); + +/* Like 'open', but ensures that the returned file descriptor has the + close-on-exec flag set. */ + +extern int gdb_open_cloexec (const char *filename, int flags, + /* mode_t */ unsigned long mode); + +/* Like mkstemp, but ensures that the file descriptor is + close-on-exec. */ + +static inline int +gdb_mkostemp_cloexec (char *name_template, int flags = 0) +{ + /* gnulib provides a mkostemp replacement if needed. */ + return mkostemp (name_template, flags | O_CLOEXEC); +} + +/* Convenience wrapper for the above, which takes the filename as an + std::string. */ + +static inline int +gdb_open_cloexec (const std::string &filename, int flags, + /* mode_t */ unsigned long mode) +{ + return gdb_open_cloexec (filename.c_str (), flags, mode); +} + +struct gdb_file_deleter +{ + void operator() (FILE *file) const + { + fclose (file); + } +}; + +/* A unique pointer to a FILE. */ + +typedef std::unique_ptr<FILE, gdb_file_deleter> gdb_file_up; + +/* Like 'fopen', but ensures that the returned file descriptor has the + close-on-exec flag set. */ + +extern gdb_file_up gdb_fopen_cloexec (const char *filename, + const char *opentype); + +/* Convenience wrapper for the above, which takes the filename as an + std::string. */ + +static inline gdb_file_up +gdb_fopen_cloexec (const std::string &filename, const char *opentype) +{ + return gdb_fopen_cloexec (filename.c_str (), opentype); +} + +/* Like 'socketpair', but ensures that the returned file descriptors + have the close-on-exec flag set. */ + +extern int gdb_socketpair_cloexec (int domain, int style, int protocol, + int filedes[2]); + +/* Like 'socket', but ensures that the returned file descriptor has + the close-on-exec flag set. */ + +extern int gdb_socket_cloexec (int domain, int style, int protocol); + +/* Like 'pipe', but ensures that the returned file descriptors have + the close-on-exec flag set. */ + +extern int gdb_pipe_cloexec (int filedes[2]); + +struct gdb_dir_deleter +{ + void operator() (DIR *dir) const + { + closedir (dir); + } +}; + +/* A unique pointer to a DIR. */ + +typedef std::unique_ptr<DIR, gdb_dir_deleter> gdb_dir_up; + +/* Return true if the file NAME exists and is a regular file. + If the result is false then *ERRNO_PTR is set to a useful value assuming + we're expecting a regular file. */ +extern bool is_regular_file (const char *name, int *errno_ptr); + + +/* A cheap (as in low-quality) recursive mkdir. Try to create all the + parents directories up to DIR and DIR itself. Stop if we hit an + error along the way. There is no attempt to remove created + directories in case of failure. + + Returns false on failure and sets errno. */ + +extern bool mkdir_recursive (const char *dir); + +#endif /* COMMON_FILESTUFF_H */ diff --git a/gdb/gdbsupport/filtered-iterator.h b/gdb/gdbsupport/filtered-iterator.h new file mode 100644 index 00000000000..e1b486d6f08 --- /dev/null +++ b/gdb/gdbsupport/filtered-iterator.h @@ -0,0 +1,87 @@ +/* A forward filtered iterator for GDB, the GNU debugger. + Copyright (C) 2018-2019 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/>. */ + +#ifndef COMMON_FILTERED_ITERATOR_H +#define COMMON_FILTERED_ITERATOR_H + +/* A filtered iterator. This wraps BaseIterator and automatically + skips elements that FilterFunc filters out. Requires that + default-constructing a BaseIterator creates a valid one-past-end + iterator. */ + +template<typename BaseIterator, typename FilterFunc> +class filtered_iterator +{ +public: + typedef filtered_iterator self_type; + typedef typename BaseIterator::value_type value_type; + typedef typename BaseIterator::reference reference; + typedef typename BaseIterator::pointer pointer; + typedef typename BaseIterator::iterator_category iterator_category; + typedef typename BaseIterator::difference_type difference_type; + + /* Construct by forwarding all arguments to the underlying + iterator. */ + template<typename... Args> + explicit filtered_iterator (Args &&...args) + : m_it (std::forward<Args> (args)...) + { skip_filtered (); } + + /* Create a one-past-end iterator. */ + filtered_iterator () = default; + + /* Need these as the variadic constructor would be a better match + otherwise. */ + filtered_iterator (filtered_iterator &) = default; + filtered_iterator (const filtered_iterator &) = default; + filtered_iterator (filtered_iterator &&) = default; + filtered_iterator (const filtered_iterator &&other) + : filtered_iterator (static_cast<const filtered_iterator &> (other)) + {} + + value_type operator* () const { return *m_it; } + + self_type &operator++ () + { + ++m_it; + skip_filtered (); + return *this; + } + + bool operator== (const self_type &other) const + { return *m_it == *other.m_it; } + + bool operator!= (const self_type &other) const + { return *m_it != *other.m_it; } + +private: + + void skip_filtered () + { + for (; m_it != m_end; ++m_it) + if (m_filter (*m_it)) + break; + } + +private: + FilterFunc m_filter {}; + BaseIterator m_it {}; + BaseIterator m_end {}; +}; + +#endif /* COMMON_FILTERED_ITERATOR_H */ diff --git a/gdb/gdbsupport/format.c b/gdb/gdbsupport/format.c new file mode 100644 index 00000000000..fb3421e62bf --- /dev/null +++ b/gdb/gdbsupport/format.c @@ -0,0 +1,350 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2019 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 "format.h" + +format_pieces::format_pieces (const char **arg) +{ + const char *s; + char *f, *string; + const char *prev_start; + const char *percent_loc; + char *sub_start, *current_substring; + enum argclass this_argclass; + + s = *arg; + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + + while (*s != '"' && *s != '\0') + { + int c = *s++; + switch (c) + { + case '\0': + continue; + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'a': + *f++ = '\a'; + break; + case 'b': + *f++ = '\b'; + break; + case 'e': + *f++ = '\e'; + break; + case 'f': + *f++ = '\f'; + break; + case 'n': + *f++ = '\n'; + break; + case 'r': + *f++ = '\r'; + break; + case 't': + *f++ = '\t'; + break; + case 'v': + *f++ = '\v'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences. */ + error (_("Unrecognized escape character \\%c in format string."), + c); + } + break; + + default: + *f++ = c; + } + } + + /* Terminate our escape-processed copy. */ + *f++ = '\0'; + + /* Whether the format string ended with double-quote or zero, we're + done with it; it's up to callers to complain about syntax. */ + *arg = s; + + /* Need extra space for the '\0's. Doubling the size is sufficient. */ + + current_substring = (char *) xmalloc (strlen (string) * 2 + 1000); + m_storage.reset (current_substring); + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass classifies the %-specs so we can give printf-type functions + something of the right size. */ + + f = string; + prev_start = string; + while (*f) + if (*f++ == '%') + { + int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; + int seen_space = 0, seen_plus = 0; + int seen_big_l = 0, seen_h = 0, seen_big_h = 0; + int seen_big_d = 0, seen_double_big_d = 0; + int bad = 0; + + /* Skip over "%%", it will become part of a literal piece. */ + if (*f == '%') + { + f++; + continue; + } + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - 1 - prev_start); + current_substring += f - 1 - prev_start; + *current_substring++ = '\0'; + + m_pieces.emplace_back (sub_start, literal_piece); + + percent_loc = f - 1; + + /* Check the validity of the format specifier, and work + out what argument it expects. We only accept C89 + format strings, with the exception of long long (which + we autoconf for). */ + + /* The first part of a format specifier is a set of flag + characters. */ + while (*f != '\0' && strchr ("0-+ #", *f)) + { + if (*f == '#') + seen_hash = 1; + else if (*f == '0') + seen_zero = 1; + else if (*f == ' ') + seen_space = 1; + else if (*f == '+') + seen_plus = 1; + f++; + } + + /* The next part of a format specifier is a width. */ + while (*f != '\0' && strchr ("0123456789", *f)) + f++; + + /* The next part of a format specifier is a precision. */ + if (*f == '.') + { + seen_prec = 1; + f++; + while (*f != '\0' && strchr ("0123456789", *f)) + f++; + } + + /* The next part of a format specifier is a length modifier. */ + if (*f == 'h') + { + seen_h = 1; + f++; + } + else if (*f == 'l') + { + f++; + lcount++; + if (*f == 'l') + { + f++; + lcount++; + } + } + else if (*f == 'L') + { + seen_big_l = 1; + f++; + } + /* Decimal32 modifier. */ + else if (*f == 'H') + { + seen_big_h = 1; + f++; + } + /* Decimal64 and Decimal128 modifiers. */ + else if (*f == 'D') + { + f++; + + /* Check for a Decimal128. */ + if (*f == 'D') + { + f++; + seen_double_big_d = 1; + } + else + seen_big_d = 1; + } + + switch (*f) + { + case 'u': + if (seen_hash) + bad = 1; + /* FALLTHROUGH */ + + case 'o': + case 'x': + case 'X': + if (seen_space || seen_plus) + bad = 1; + /* FALLTHROUGH */ + + case 'd': + case 'i': + if (lcount == 0) + this_argclass = int_arg; + else if (lcount == 1) + this_argclass = long_arg; + else + this_argclass = long_long_arg; + + if (seen_big_l) + bad = 1; + break; + + case 'c': + this_argclass = lcount == 0 ? int_arg : wide_char_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'p': + this_argclass = ptr_arg; + if (lcount || seen_h || seen_big_l) + bad = 1; + if (seen_prec) + bad = 1; + if (seen_hash || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 's': + this_argclass = lcount == 0 ? string_arg : wide_string_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + if (seen_double_big_d) + this_argclass = dec128float_arg; + else if (seen_big_d) + this_argclass = dec64float_arg; + else if (seen_big_h) + this_argclass = dec32float_arg; + else if (seen_big_l) + this_argclass = long_double_arg; + else + this_argclass = double_arg; + + if (lcount || seen_h) + bad = 1; + break; + + case '*': + error (_("`*' not supported for precision or width in printf")); + + case 'n': + error (_("Format specifier `n' not supported in printf")); + + case '\0': + error (_("Incomplete format specifier at end of format string")); + + default: + error (_("Unrecognized format specifier '%c' in printf"), *f); + } + + if (bad) + error (_("Inappropriate modifiers to " + "format specifier '%c' in printf"), + *f); + + f++; + + sub_start = current_substring; + + if (lcount > 1 && USE_PRINTF_I64) + { + /* Windows' printf does support long long, but not the usual way. + Convert %lld to %I64d. */ + int length_before_ll = f - percent_loc - 1 - lcount; + + strncpy (current_substring, percent_loc, length_before_ll); + strcpy (current_substring + length_before_ll, "I64"); + current_substring[length_before_ll + 3] = + percent_loc[length_before_ll + lcount]; + current_substring += length_before_ll + 4; + } + else if (this_argclass == wide_string_arg + || this_argclass == wide_char_arg) + { + /* Convert %ls or %lc to %s. */ + int length_before_ls = f - percent_loc - 2; + + strncpy (current_substring, percent_loc, length_before_ls); + strcpy (current_substring + length_before_ls, "s"); + current_substring += length_before_ls + 2; + } + else + { + strncpy (current_substring, percent_loc, f - percent_loc); + current_substring += f - percent_loc; + } + + *current_substring++ = '\0'; + + prev_start = f; + + m_pieces.emplace_back (sub_start, this_argclass); + } + + /* Record the remainder of the string. */ + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - prev_start); + current_substring += f - prev_start; + *current_substring++ = '\0'; + + m_pieces.emplace_back (sub_start, literal_piece); +} diff --git a/gdb/gdbsupport/format.h b/gdb/gdbsupport/format.h new file mode 100644 index 00000000000..08ef66a7602 --- /dev/null +++ b/gdb/gdbsupport/format.h @@ -0,0 +1,96 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_FORMAT_H +#define COMMON_FORMAT_H + +#include "gdbsupport/gdb_string_view.h" + +#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) +# define USE_PRINTF_I64 1 +# define PRINTF_HAS_LONG_LONG +#else +# define USE_PRINTF_I64 0 +#endif + +/* The argclass represents the general type of data that goes with a + format directive; int_arg for %d, long_arg for %l, and so forth. + Note that these primarily distinguish types by size and need for + special handling, so for instance %u and %x are (at present) also + classed as int_arg. */ + +enum argclass + { + literal_piece, + int_arg, long_arg, long_long_arg, ptr_arg, + string_arg, wide_string_arg, wide_char_arg, + double_arg, long_double_arg, + dec32float_arg, dec64float_arg, dec128float_arg + }; + +/* A format piece is a section of the format string that may include a + single print directive somewhere in it, and the associated class + for the argument. */ + +struct format_piece +{ + format_piece (const char *str, enum argclass argc) + : string (str), + argclass (argc) + { + } + + bool operator== (const format_piece &other) const + { + return (this->argclass == other.argclass + && gdb::string_view (this->string) == other.string); + } + + const char *string; + enum argclass argclass; +}; + +class format_pieces +{ +public: + + format_pieces (const char **arg); + ~format_pieces () = default; + + DISABLE_COPY_AND_ASSIGN (format_pieces); + + typedef std::vector<format_piece>::iterator iterator; + + iterator begin () + { + return m_pieces.begin (); + } + + iterator end () + { + return m_pieces.end (); + } + +private: + + std::vector<format_piece> m_pieces; + gdb::unique_xmalloc_ptr<char> m_storage; +}; + +#endif /* COMMON_FORMAT_H */ diff --git a/gdb/gdbsupport/forward-scope-exit.h b/gdb/gdbsupport/forward-scope-exit.h new file mode 100644 index 00000000000..bba4027aa5a --- /dev/null +++ b/gdb/gdbsupport/forward-scope-exit.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2019 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/>. */ + +#ifndef COMMON_FORWARD_SCOPE_EXIT_H +#define COMMON_FORWARD_SCOPE_EXIT_H + +#include "gdbsupport/scope-exit.h" +#include <functional> + +/* A forward_scope_exit is like scope_exit, but instead of giving it a + callable, you instead specialize it for a given cleanup function, + and the generated class automatically has a constructor with the + same interface as the cleanup function. forward_scope_exit + captures the arguments passed to the ctor, and in turn passes those + as arguments to the wrapped cleanup function, when it is called at + scope exit time, from within the forward_scope_exit dtor. The + forward_scope_exit class can take any number of arguments, and is + cancelable if needed. + + This allows usage like this: + + void + delete_longjmp_breakpoint (int arg) + { + // Blah, blah, blah... + } + + using longjmp_breakpoint_cleanup + = FORWARD_SCOPE_EXIT (delete_longjmp_breakpoint); + + This above created a new cleanup class `longjmp_breakpoint_cleanup` + than can then be used like this: + + longjmp_breakpoint_cleanup obj (thread); + + // Blah, blah, blah... + + obj.release (); // Optional cancel if needed. + + forward_scope_exit is also handy when you would need to wrap a + scope_exit in a gdb::optional: + + gdb::optional<longjmp_breakpoint_cleanup> cleanup; + if (some condition) + cleanup.emplace (thread); + ... + if (cleanup) + cleanup->release (); + + since with scope exit, you would have to know the scope_exit's + callable template type when you create the gdb::optional: + + gdb:optional<scope_exit<what goes here?>> + + The "forward" naming fits both purposes shown above -- the class + "forwards" ctor arguments to the wrapped cleanup function at scope + exit time, and can also be used to "forward declare" + scope_exit-like objects. */ + +namespace detail +{ + +/* Function and Signature are passed in the same type, in order to + extract Function's arguments' types in the specialization below. + Those are used to generate the constructor. */ + +template<typename Function, Function *function, typename Signature> +struct forward_scope_exit; + +template<typename Function, Function *function, + typename Res, typename... Args> +class forward_scope_exit<Function, function, Res (Args...)> + : public scope_exit_base<forward_scope_exit<Function, + function, + Res (Args...)>> +{ + /* For access to on_exit(). */ + friend scope_exit_base<forward_scope_exit<Function, + function, + Res (Args...)>>; + +public: + explicit forward_scope_exit (Args ...args) + : m_bind_function (function, args...) + { + /* Nothing. */ + } + +private: + void on_exit () + { + m_bind_function (); + } + + /* The function and the arguments passed to the ctor, all packed in + a std::bind. */ + decltype (std::bind (function, std::declval<Args> ()...)) + m_bind_function; +}; + +} /* namespace detail */ + +/* This is the "public" entry point. It's a macro to avoid having to + name FUNC more than once. */ + +#define FORWARD_SCOPE_EXIT(FUNC) \ + detail::forward_scope_exit<decltype (FUNC), FUNC, decltype (FUNC)> + +#endif /* COMMON_FORWARD_SCOPE_EXIT_H */ diff --git a/gdb/gdbsupport/function-view.h b/gdb/gdbsupport/function-view.h new file mode 100644 index 00000000000..1a577ba0e91 --- /dev/null +++ b/gdb/gdbsupport/function-view.h @@ -0,0 +1,323 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_FUNCTION_VIEW_H +#define COMMON_FUNCTION_VIEW_H + +/* function_view is a polymorphic type-erasing wrapper class that + encapsulates a non-owning reference to arbitrary callable objects. + + A way to put it is that function_view is to std::function like + std::string_view is to std::string. While std::function stores a + type-erased callable object internally, function_view holds a + type-erased reference to an external callable object. + + This is meant to be used as callback type of a function that: + + #1 - Takes a callback as parameter. + + #2 - Wants to support arbitrary callable objects as callback type + (e.g., stateful function objects, lambda closures, free + functions). + + #3 - Does not store the callback anywhere; instead the function + just calls the callback directly or forwards it to some + other function that calls it. + + #4 - Can't be, or we don't want it to be, a template function + with the callable type as template parameter. For example, + when the callback is a parameter of a virtual member + function, or when putting the function template in a header + would expose too much implementation detail. + + Note that the C-style "function pointer" + "void *data" callback + parameter idiom fails requirement #2 above. Please don't add new + uses of that idiom. I.e., something like this wouldn't work; + + typedef bool (iterate_over_foos_cb) (foo *f, void *user_data), + void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data); + + foo *find_foo_by_type (int type) + { + foo *found = nullptr; + + iterate_over_foos ([&] (foo *f, void *data) + { + if (foo->type == type) + { + found = foo; + return true; // stop iterating + } + return false; // continue iterating + }, NULL); + + return found; + } + + The above wouldn't compile, because lambdas with captures can't be + implicitly converted to a function pointer (because a capture means + some context data must be passed to the lambda somehow). + + C++11 gave us std::function as type-erased wrapper around arbitrary + callables, however, std::function is not an ideal fit for transient + callbacks such as the use case above. For this use case, which is + quite pervasive, a function_view is a better choice, because while + function_view is light and does not require any heap allocation, + std::function is a heavy-weight object with value semantics that + generally requires a heap allocation on construction/assignment of + the target callable. In addition, while it is possible to use + std::function in such a way that avoids most of the overhead by + making sure to only construct it with callables of types that fit + std::function's small object optimization, such as function + pointers and std::reference_wrapper callables, that is quite + inconvenient in practice, because restricting to free-function + callables would imply no state/capture/closure, which we need in + most cases, and std::reference_wrapper implies remembering to use + std::ref/std::cref where the callable is constructed, with the + added inconvenience that std::ref/std::cref have deleted rvalue-ref + overloads, meaning you can't use unnamed/temporary lambdas with + them. + + Note that because function_view is a non-owning view of a callable, + care must be taken to ensure that the callable outlives the + function_view that calls it. This is not really a problem for the + use case function_view is intended for, such as passing a temporary + function object / lambda to a function that accepts a callback, + because in those cases, the temporary is guaranteed to be live + until the called function returns. + + Calling a function_view with no associated target is undefined, + unlike with std::function, which throws std::bad_function_call. + This is by design, to avoid the otherwise necessary NULL check in + function_view::operator(). + + Since function_view objects are small (a pair of pointers), they + should generally be passed around by value. + + Usage: + + Given this function that accepts a callback: + + void + iterate_over_foos (gdb::function_view<void (foo *)> callback) + { + for (auto &foo : foos) + callback (&foo); + } + + you can call it like this, passing a lambda as callback: + + iterate_over_foos ([&] (foo *f) + { + process_one_foo (f); + }); + + or like this, passing a function object as callback: + + struct function_object + { + void operator() (foo *f) + { + if (s->check ()) + process_one_foo (f); + } + + // some state + state *s; + }; + + state mystate; + function_object matcher {&mystate}; + iterate_over_foos (matcher); + + or like this, passing a function pointer as callback: + + iterate_over_foos (process_one_foo); + + You can find unit tests covering the whole API in + unittests/function-view-selftests.c. */ + +namespace gdb { + +namespace fv_detail { +/* Bits shared by all function_view instantiations that do not depend + on the template parameters. */ + +/* Storage for the erased callable. This is a union in order to be + able to save both a function object (data) pointer or a function + pointer without triggering undefined behavior. */ +union erased_callable +{ + /* For function objects. */ + void *data; + + /* For function pointers. */ + void (*fn) (); +}; + +} /* namespace fv_detail */ + +/* Use partial specialization to get access to the callable's + signature. */ +template<class Signature> +struct function_view; + +template<typename Res, typename... Args> +class function_view<Res (Args...)> +{ + template<typename From, typename To> + using CompatibleReturnType + = Or<std::is_void<To>, + std::is_same<From, To>, + std::is_convertible<From, To>>; + + /* True if Func can be called with Args, and either the result is + Res, convertible to Res or Res is void. */ + template<typename Callable, + typename Res2 = typename std::result_of<Callable &(Args...)>::type> + struct IsCompatibleCallable : CompatibleReturnType<Res2, Res> + {}; + + /* True if Callable is a function_view. Used to avoid hijacking the + copy ctor. */ + template <typename Callable> + struct IsFunctionView + : std::is_same<function_view, typename std::decay<Callable>::type> + {}; + + public: + + /* NULL by default. */ + constexpr function_view () noexcept + : m_erased_callable {}, + m_invoker {} + {} + + /* Default copy/assignment is fine. */ + function_view (const function_view &) = default; + function_view &operator= (const function_view &) = default; + + /* This is the main entry point. Use SFINAE to avoid hijacking the + copy constructor and to ensure that the target type is + compatible. */ + template + <typename Callable, + typename = Requires<Not<IsFunctionView<Callable>>>, + typename = Requires<IsCompatibleCallable<Callable>>> + function_view (Callable &&callable) noexcept + { + bind (callable); + } + + /* Construct a NULL function_view. */ + constexpr function_view (std::nullptr_t) noexcept + : m_erased_callable {}, + m_invoker {} + {} + + /* Clear a function_view. */ + function_view &operator= (std::nullptr_t) noexcept + { + m_invoker = nullptr; + return *this; + } + + /* Return true if the wrapper has a target, false otherwise. Note + we check M_INVOKER instead of M_ERASED_CALLABLE because we don't + know which member of the union is active right now. */ + constexpr explicit operator bool () const noexcept + { return m_invoker != nullptr; } + + /* Call the callable. */ + Res operator () (Args... args) const + { return m_invoker (m_erased_callable, std::forward<Args> (args)...); } + + private: + + /* Bind this function_view to a compatible function object + reference. */ + template <typename Callable> + void bind (Callable &callable) noexcept + { + m_erased_callable.data = (void *) std::addressof (callable); + m_invoker = [] (fv_detail::erased_callable ecall, Args... args) + noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res + { + auto &restored_callable = *static_cast<Callable *> (ecall.data); + /* The explicit cast to Res avoids a compile error when Res is + void and the callable returns non-void. */ + return (Res) restored_callable (std::forward<Args> (args)...); + }; + } + + /* Bind this function_view to a compatible function pointer. + + Making this a separate function allows avoiding one indirection, + by storing the function pointer directly in the storage, instead + of a pointer to pointer. erased_callable is then a union in + order to avoid storing a function pointer as a data pointer here, + which would be undefined. */ + template<class Res2, typename... Args2> + void bind (Res2 (*fn) (Args2...)) noexcept + { + m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn); + m_invoker = [] (fv_detail::erased_callable ecall, Args... args) + noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res + { + auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn); + /* The explicit cast to Res avoids a compile error when Res is + void and the callable returns non-void. */ + return (Res) restored_fn (std::forward<Args> (args)...); + }; + } + + /* Storage for the erased callable. */ + fv_detail::erased_callable m_erased_callable; + + /* The invoker. This is set to a capture-less lambda by one of the + 'bind' overloads. The lambda restores the right type of the + callable (which is passed as first argument), and forwards the + args. */ + Res (*m_invoker) (fv_detail::erased_callable, Args...); +}; + +/* Allow comparison with NULL. Defer the work to the in-class + operator bool implementation. */ + +template<typename Res, typename... Args> +constexpr inline bool +operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept +{ return !static_cast<bool> (f); } + +template<typename Res, typename... Args> +constexpr inline bool +operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept +{ return !static_cast<bool> (f); } + +template<typename Res, typename... Args> +constexpr inline bool +operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept +{ return static_cast<bool> (f); } + +template<typename Res, typename... Args> +constexpr inline bool +operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept +{ return static_cast<bool> (f); } + +} /* namespace gdb */ + +#endif diff --git a/gdb/gdbsupport/gdb_assert.h b/gdb/gdbsupport/gdb_assert.h new file mode 100644 index 00000000000..a719d878990 --- /dev/null +++ b/gdb/gdbsupport/gdb_assert.h @@ -0,0 +1,59 @@ +/* GDB-friendly replacement for <assert.h>. + Copyright (C) 2000-2019 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/>. */ + +#ifndef COMMON_GDB_ASSERT_H +#define COMMON_GDB_ASSERT_H + +/* A static assertion. This will cause a compile-time error if EXPR, + which must be a compile-time constant, is false. */ + +#define gdb_static_assert(expr) static_assert (expr, "") + +/* PRAGMATICS: "gdb_assert.h":gdb_assert() is a lower case (rather + than upper case) macro since that provides the closest fit to the + existing lower case macro <assert.h>:assert() that it is + replacing. */ + +#define gdb_assert(expr) \ + ((void) ((expr) ? 0 : \ + (gdb_assert_fail (#expr, __FILE__, __LINE__, FUNCTION_NAME), 0))) + +/* This prints an "Assertion failed" message, asking the user if they + want to continue, dump core, or just exit. */ +#if defined (FUNCTION_NAME) +#define gdb_assert_fail(assertion, file, line, function) \ + internal_error (file, line, _("%s: Assertion `%s' failed."), \ + function, assertion) +#else +#define gdb_assert_fail(assertion, file, line, function) \ + internal_error (file, line, _("Assertion `%s' failed."), \ + assertion) +#endif + +/* The canonical form of gdb_assert (0). + MESSAGE is a string to include in the error message. */ + +#if defined (FUNCTION_NAME) +#define gdb_assert_not_reached(message) \ + internal_error (__FILE__, __LINE__, "%s: %s", FUNCTION_NAME, _(message)) +#else +#define gdb_assert_not_reached(message) \ + internal_error (__FILE__, __LINE__, _(message)) +#endif + +#endif /* COMMON_GDB_ASSERT_H */ diff --git a/gdb/gdbsupport/gdb_locale.h b/gdb/gdbsupport/gdb_locale.h new file mode 100644 index 00000000000..4e59b4f16cb --- /dev/null +++ b/gdb/gdbsupport/gdb_locale.h @@ -0,0 +1,43 @@ +/* GDB-friendly replacement for <locale.h>. + Copyright (C) 2002-2019 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/>. */ + +#ifndef COMMON_GDB_LOCALE_H +#define COMMON_GDB_LOCALE_H + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(String) gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define _(String) (String) +# define N_(String) (String) +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif + +#endif /* COMMON_GDB_LOCALE_H */ diff --git a/gdb/gdbsupport/gdb_optional.h b/gdb/gdbsupport/gdb_optional.h new file mode 100644 index 00000000000..94300e48d6a --- /dev/null +++ b/gdb/gdbsupport/gdb_optional.h @@ -0,0 +1,219 @@ +/* An optional object. + + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_GDB_OPTIONAL_H +#define COMMON_GDB_OPTIONAL_H + +#include "gdbsupport/traits.h" + +namespace gdb +{ + +struct in_place_t +{ + explicit in_place_t () = default; +}; + +constexpr gdb::in_place_t in_place {}; + +/* This class attempts to be a compatible subset of std::optional, + which is slated to be available in C++17. This class optionally + holds an object of some type -- by default it is constructed not + holding an object, but later the object can be "emplaced". This is + similar to using std::unique_ptr, but in-object allocation is + guaranteed. + + Unlike std::optional, we currently only support copy/move + construction/assignment of an optional<T> from either exactly + optional<T> or T. I.e., we don't support copy/move + construction/assignment from optional<U> or U, when U is a type + convertible to T. Making that work depending on the definitions of + T and U is somewhat complicated, and currently the users of this + class don't need it. */ + +template<typename T> +class optional +{ +public: + + constexpr optional () + : m_dummy () + {} + + template<typename... Args> + constexpr optional (in_place_t, Args &&... args) + : m_item (std::forward<Args> (args)...), + m_instantiated (true) + {} + + ~optional () + { this->reset (); } + + /* Copy and move constructors. */ + + optional (const optional &other) + { + if (other.m_instantiated) + this->emplace (other.get ()); + } + + optional (optional &&other) + noexcept(std::is_nothrow_move_constructible<T> ()) + { + if (other.m_instantiated) + this->emplace (std::move (other.get ())); + } + + constexpr optional (const T &other) + : m_item (other), + m_instantiated (true) + {} + + constexpr optional (T &&other) + noexcept (std::is_nothrow_move_constructible<T> ()) + : m_item (std::move (other)), + m_instantiated (true) + {} + + /* Assignment operators. */ + + optional & + operator= (const optional &other) + { + if (m_instantiated && other.m_instantiated) + this->get () = other.get (); + else + { + if (other.m_instantiated) + this->emplace (other.get ()); + else + this->reset (); + } + + return *this; + } + + optional & + operator= (optional &&other) + noexcept (And<std::is_nothrow_move_constructible<T>, + std::is_nothrow_move_assignable<T>> ()) + { + if (m_instantiated && other.m_instantiated) + this->get () = std::move (other.get ()); + else + { + if (other.m_instantiated) + this->emplace (std::move (other.get ())); + else + this->reset (); + } + return *this; + } + + optional & + operator= (const T &other) + { + if (m_instantiated) + this->get () = other; + else + this->emplace (other); + return *this; + } + + optional & + operator= (T &&other) + noexcept (And<std::is_nothrow_move_constructible<T>, + std::is_nothrow_move_assignable<T>> ()) + { + if (m_instantiated) + this->get () = std::move (other); + else + this->emplace (std::move (other)); + return *this; + } + + template<typename... Args> + T &emplace (Args &&... args) + { + this->reset (); + new (&m_item) T (std::forward<Args>(args)...); + m_instantiated = true; + return this->get (); + } + + /* Observers. */ + constexpr const T *operator-> () const + { return std::addressof (this->get ()); } + + T *operator-> () + { return std::addressof (this->get ()); } + + constexpr const T &operator* () const & + { return this->get (); } + + T &operator* () & + { return this->get (); } + + T &&operator* () && + { return std::move (this->get ()); } + + constexpr const T &&operator* () const && + { return std::move (this->get ()); } + + constexpr explicit operator bool () const noexcept + { return m_instantiated; } + + constexpr bool has_value () const noexcept + { return m_instantiated; } + + /* 'reset' is a 'safe' operation with no precondition. */ + void reset () noexcept + { + if (m_instantiated) + this->destroy (); + } + +private: + + /* Destroy the object. */ + void destroy () + { + gdb_assert (m_instantiated); + m_instantiated = false; + m_item.~T (); + } + + /* The get operations have m_instantiated as a precondition. */ + T &get () noexcept { return m_item; } + constexpr const T &get () const noexcept { return m_item; } + + /* The object. */ + union + { + struct { } m_dummy; + T m_item; + }; + + /* True if the object was ever emplaced. */ + bool m_instantiated = false; +}; + +} + +#endif /* COMMON_GDB_OPTIONAL_H */ diff --git a/gdb/gdbsupport/gdb_proc_service.h b/gdb/gdbsupport/gdb_proc_service.h new file mode 100644 index 00000000000..8565da3286c --- /dev/null +++ b/gdb/gdbsupport/gdb_proc_service.h @@ -0,0 +1,173 @@ +/* <proc_service.h> replacement for systems that don't have it. + Copyright (C) 2000-2019 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/>. */ + +#ifndef COMMON_GDB_PROC_SERVICE_H +#define COMMON_GDB_PROC_SERVICE_H + +#include <sys/types.h> + +#ifdef HAVE_PROC_SERVICE_H + +/* glibc's proc_service.h doesn't wrap itself with extern "C". Need + to do it ourselves. */ +EXTERN_C_PUSH + +#include <proc_service.h> + +EXTERN_C_POP + +#else /* HAVE_PROC_SERVICE_H */ + +/* The following fallback definitions have been imported and adjusted + from glibc's proc_service.h */ + +/* Callback interface for libthread_db, functions users must define. + Copyright (C) 1999,2002,2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* The definitions in this file must correspond to those in the debugger. */ + +#ifdef HAVE_SYS_PROCFS_H +#include <sys/procfs.h> +#endif + +/* Not all platforms bring in <linux/elf.h> via <sys/procfs.h>. If + <sys/procfs.h> wasn't enough to find elf_fpregset_t, try the kernel + headers also (but don't if we don't need to). */ +#ifndef HAVE_ELF_FPREGSET_T +# ifdef HAVE_LINUX_ELF_H +# include <linux/elf.h> +# endif +#endif + +EXTERN_C_PUSH + +/* Functions in this interface return one of these status codes. */ +typedef enum +{ + PS_OK, /* Generic "call succeeded". */ + PS_ERR, /* Generic error. */ + PS_BADPID, /* Bad process handle. */ + PS_BADLID, /* Bad LWP identifier. */ + PS_BADADDR, /* Bad address. */ + PS_NOSYM, /* Could not find given symbol. */ + PS_NOFREGS /* FPU register set not available for given LWP. */ +} ps_err_e; + +#ifndef HAVE_LWPID_T +typedef unsigned int lwpid_t; +#endif + +#ifndef HAVE_PSADDR_T +typedef void *psaddr_t; +#endif + +#ifndef HAVE_PRGREGSET_T +typedef elf_gregset_t prgregset_t; +#endif + +#ifndef HAVE_PRFPREGSET_T +typedef elf_fpregset_t prfpregset_t; +#endif + +/* This type is opaque in this interface. It's defined by the user of + libthread_db. GDB's version is defined below. */ +struct ps_prochandle; + + +/* Read or write process memory at the given address. */ +extern ps_err_e ps_pdread (struct ps_prochandle *, + psaddr_t, void *, size_t); +extern ps_err_e ps_pdwrite (struct ps_prochandle *, + psaddr_t, const void *, size_t); +extern ps_err_e ps_ptread (struct ps_prochandle *, + psaddr_t, void *, size_t); +extern ps_err_e ps_ptwrite (struct ps_prochandle *, + psaddr_t, const void *, size_t); + + +/* Get and set the given LWP's general or FPU register set. */ +extern ps_err_e ps_lgetregs (struct ps_prochandle *, + lwpid_t, prgregset_t); +extern ps_err_e ps_lsetregs (struct ps_prochandle *, + lwpid_t, const prgregset_t); +extern ps_err_e ps_lgetfpregs (struct ps_prochandle *, + lwpid_t, prfpregset_t *); +extern ps_err_e ps_lsetfpregs (struct ps_prochandle *, + lwpid_t, const prfpregset_t *); + +/* Return the PID of the process. */ +extern pid_t ps_getpid (struct ps_prochandle *); + +/* Fetch the special per-thread address associated with the given LWP. + This call is only used on a few platforms (most use a normal register). + The meaning of the `int' parameter is machine-dependent. */ +extern ps_err_e ps_get_thread_area (struct ps_prochandle *, + lwpid_t, int, psaddr_t *); + + +/* Look up the named symbol in the named DSO in the symbol tables + associated with the process being debugged, filling in *SYM_ADDR + with the corresponding run-time address. */ +extern ps_err_e ps_pglobal_lookup (struct ps_prochandle *, + const char *object_name, + const char *sym_name, + psaddr_t *sym_addr); + + +/* Stop or continue the entire process. */ +extern ps_err_e ps_pstop (struct ps_prochandle *); +extern ps_err_e ps_pcontinue (struct ps_prochandle *); + +/* Stop or continue the given LWP alone. */ +extern ps_err_e ps_lstop (struct ps_prochandle *, lwpid_t); +extern ps_err_e ps_lcontinue (struct ps_prochandle *, lwpid_t); + +/* The following are only defined in/called by Solaris. */ + +/* Get size of extra register set. */ +extern ps_err_e ps_lgetxregsize (struct ps_prochandle *ph, + lwpid_t lwpid, int *xregsize); +/* Get extra register set. */ +extern ps_err_e ps_lgetxregs (struct ps_prochandle *ph, lwpid_t lwpid, + caddr_t xregset); +extern ps_err_e ps_lsetxregs (struct ps_prochandle *ph, lwpid_t lwpid, + caddr_t xregset); + +/* Log a message (sends to gdb_stderr). */ +extern void ps_plog (const char *fmt, ...); + +EXTERN_C_POP + +#endif /* HAVE_PROC_SERVICE_H */ + +#endif /* COMMON_GDB_PROC_SERVICE_H */ diff --git a/gdb/gdbsupport/gdb_ref_ptr.h b/gdb/gdbsupport/gdb_ref_ptr.h new file mode 100644 index 00000000000..c31c9a2e58b --- /dev/null +++ b/gdb/gdbsupport/gdb_ref_ptr.h @@ -0,0 +1,228 @@ +/* Reference-counted smart pointer class + + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_GDB_REF_PTR_H +#define COMMON_GDB_REF_PTR_H + +#include <cstddef> + +namespace gdb +{ + +/* An instance of this class either holds a reference to a + reference-counted object or is "NULL". Reference counting is + handled externally by a policy class. If the object holds a + reference, then when the object is destroyed, the reference is + decref'd. + + Normally an instance is constructed using a pointer. This sort of + initialization lets this class manage the lifetime of that + reference. + + Assignment and copy construction will make a new reference as + appropriate. Assignment from a plain pointer is disallowed to + avoid confusion about whether this acquires a new reference; + instead use the "reset" method -- which, like the pointer + constructor, transfers ownership. + + The policy class must provide two static methods: + void incref (T *); + void decref (T *); +*/ +template<typename T, typename Policy> +class ref_ptr +{ + public: + + /* Create a new NULL instance. */ + ref_ptr () + : m_obj (NULL) + { + } + + /* Create a new NULL instance. Note that this is not explicit. */ + ref_ptr (const std::nullptr_t) + : m_obj (NULL) + { + } + + /* Create a new instance. OBJ is a reference, management of which + is now transferred to this class. */ + explicit ref_ptr (T *obj) + : m_obj (obj) + { + } + + /* Copy another instance. */ + ref_ptr (const ref_ptr &other) + : m_obj (other.m_obj) + { + if (m_obj != NULL) + Policy::incref (m_obj); + } + + /* Transfer ownership from OTHER. */ + ref_ptr (ref_ptr &&other) + : m_obj (other.m_obj) + { + other.m_obj = NULL; + } + + /* Destroy this instance. */ + ~ref_ptr () + { + if (m_obj != NULL) + Policy::decref (m_obj); + } + + /* Copy another instance. */ + ref_ptr &operator= (const ref_ptr &other) + { + /* Do nothing on self-assignment. */ + if (this != &other) + { + reset (other.m_obj); + if (m_obj != NULL) + Policy::incref (m_obj); + } + return *this; + } + + /* Transfer ownership from OTHER. */ + ref_ptr &operator= (ref_ptr &&other) + { + /* Do nothing on self-assignment. */ + if (this != &other) + { + reset (other.m_obj); + other.m_obj = NULL; + } + return *this; + } + + /* Change this instance's referent. OBJ is a reference, management + of which is now transferred to this class. */ + void reset (T *obj) + { + if (m_obj != NULL) + Policy::decref (m_obj); + m_obj = obj; + } + + /* Return this instance's referent without changing the state of + this class. */ + T *get () const + { + return m_obj; + } + + /* Return this instance's referent, and stop managing this + reference. The caller is now responsible for the ownership of + the reference. */ + ATTRIBUTE_UNUSED_RESULT T *release () + { + T *result = m_obj; + + m_obj = NULL; + return result; + } + + /* Let users refer to members of the underlying pointer. */ + T *operator-> () const + { + return m_obj; + } + + /* Acquire a new reference and return a ref_ptr that owns it. */ + static ref_ptr<T, Policy> new_reference (T *obj) + { + Policy::incref (obj); + return ref_ptr<T, Policy> (obj); + } + + private: + + T *m_obj; +}; + +template<typename T, typename Policy> +inline bool operator== (const ref_ptr<T, Policy> &lhs, + const ref_ptr<T, Policy> &rhs) +{ + return lhs.get () == rhs.get (); +} + +template<typename T, typename Policy> +inline bool operator== (const ref_ptr<T, Policy> &lhs, const T *rhs) +{ + return lhs.get () == rhs; +} + +template<typename T, typename Policy> +inline bool operator== (const ref_ptr<T, Policy> &lhs, const std::nullptr_t) +{ + return lhs.get () == nullptr; +} + +template<typename T, typename Policy> +inline bool operator== (const T *lhs, const ref_ptr<T, Policy> &rhs) +{ + return lhs == rhs.get (); +} + +template<typename T, typename Policy> +inline bool operator== (const std::nullptr_t, const ref_ptr<T, Policy> &rhs) +{ + return nullptr == rhs.get (); +} + +template<typename T, typename Policy> +inline bool operator!= (const ref_ptr<T, Policy> &lhs, + const ref_ptr<T, Policy> &rhs) +{ + return lhs.get () != rhs.get (); +} + +template<typename T, typename Policy> +inline bool operator!= (const ref_ptr<T, Policy> &lhs, const T *rhs) +{ + return lhs.get () != rhs; +} + +template<typename T, typename Policy> +inline bool operator!= (const ref_ptr<T, Policy> &lhs, const std::nullptr_t) +{ + return lhs.get () != nullptr; +} + +template<typename T, typename Policy> +inline bool operator!= (const T *lhs, const ref_ptr<T, Policy> &rhs) +{ + return lhs != rhs.get (); +} + +template<typename T, typename Policy> +inline bool operator!= (const std::nullptr_t, const ref_ptr<T, Policy> &rhs) +{ + return nullptr != rhs.get (); +} + +} + +#endif /* COMMON_GDB_REF_PTR_H */ diff --git a/gdb/gdbsupport/gdb_setjmp.h b/gdb/gdbsupport/gdb_setjmp.h new file mode 100644 index 00000000000..d4ebbfa8f2a --- /dev/null +++ b/gdb/gdbsupport/gdb_setjmp.h @@ -0,0 +1,34 @@ +/* Portability wrappers for setjmp and longjmp. + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_GDB_SETJMP_H +#define COMMON_GDB_SETJMP_H + +#include <setjmp.h> + +#ifdef HAVE_SIGSETJMP +#define SIGJMP_BUF sigjmp_buf +#define SIGSETJMP(buf) sigsetjmp((buf), 1) +#define SIGLONGJMP(buf,val) siglongjmp((buf), (val)) +#else +#define SIGJMP_BUF jmp_buf +#define SIGSETJMP(buf) setjmp(buf) +#define SIGLONGJMP(buf,val) longjmp((buf), (val)) +#endif + +#endif /* COMMON_GDB_SETJMP_H */ diff --git a/gdb/gdbsupport/gdb_signals.h b/gdb/gdbsupport/gdb_signals.h new file mode 100644 index 00000000000..da99f57133c --- /dev/null +++ b/gdb/gdbsupport/gdb_signals.h @@ -0,0 +1,58 @@ +/* Target signal translation functions for GDB. + Copyright (C) 1990-2019 Free Software Foundation, Inc. + Contributed by Cygnus Support. + + 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/>. */ + +#ifndef COMMON_GDB_SIGNALS_H +#define COMMON_GDB_SIGNALS_H + +#include "gdb/signals.h" + +/* Predicate to gdb_signal_to_host(). Return non-zero if the enum + targ_signal SIGNO has an equivalent ``host'' representation. */ +/* FIXME: cagney/1999-11-22: The name below was chosen in preference + to the shorter gdb_signal_p() because it is far less ambigious. + In this context ``gdb_signal'' refers to GDB's internal + representation of the target's set of signals while ``host signal'' + refers to the target operating system's signal. Confused? */ +extern int gdb_signal_to_host_p (enum gdb_signal signo); + +/* Convert between host signal numbers and enum gdb_signal's. + gdb_signal_to_host() returns 0 and prints a warning() on GDB's + console if SIGNO has no equivalent host representation. */ +/* FIXME: cagney/1999-11-22: Here ``host'' is used incorrectly, it is + refering to the target operating system's signal numbering. + Similarly, ``enum gdb_signal'' is named incorrectly, ``enum + gdb_signal'' would probably be better as it is refering to GDB's + internal representation of a target operating system's signal. */ +extern enum gdb_signal gdb_signal_from_host (int); +extern int gdb_signal_to_host (enum gdb_signal); + +/* Return the enum symbol name of SIG as a string, to use in debug + output. */ +extern const char *gdb_signal_to_symbol_string (enum gdb_signal sig); + +/* Return the string for a signal. */ +extern const char *gdb_signal_to_string (enum gdb_signal); + +/* Return the name (SIGHUP, etc.) for a signal. */ +extern const char *gdb_signal_to_name (enum gdb_signal); + +/* Given a name (SIGHUP, etc.), return its signal. */ +enum gdb_signal gdb_signal_from_name (const char *); + +#endif /* COMMON_GDB_SIGNALS_H */ diff --git a/gdb/gdbsupport/gdb_splay_tree.h b/gdb/gdbsupport/gdb_splay_tree.h new file mode 100644 index 00000000000..90b05cdce17 --- /dev/null +++ b/gdb/gdbsupport/gdb_splay_tree.h @@ -0,0 +1,42 @@ +/* GDB wrapper for splay trees. + + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_GDB_SPLAY_TREE_H +#define COMMON_GDB_SPLAY_TREE_H + +#include "splay-tree.h" + +namespace gdb { + +struct splay_tree_deleter +{ + void operator() (splay_tree tree) const + { + splay_tree_delete (tree); + } +}; + +} /* namespace gdb */ + +/* A unique pointer to a splay tree. */ + +typedef std::unique_ptr<splay_tree_s, gdb::splay_tree_deleter> + gdb_splay_tree_up; + +#endif /* COMMON_GDB_SPLAY_TREE_H */ diff --git a/gdb/gdbsupport/gdb_string_view.h b/gdb/gdbsupport/gdb_string_view.h new file mode 100644 index 00000000000..68f7f7de36b --- /dev/null +++ b/gdb/gdbsupport/gdb_string_view.h @@ -0,0 +1,562 @@ +// Components for manipulating non-owning sequences of characters -*- C++ -*- + + +#ifndef COMMON_GDB_STRING_VIEW_H +#define COMMON_GDB_STRING_VIEW_H + +// Note: This file has been stolen from the gcc repo +// (libstdc++-v3/include/experimental/string_view) and has local modifications. + +// Copyright (C) 2013-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +// +// N3762 basic_string_view library +// + + +#if __cplusplus >= 201703L + +#include <string_view> + +namespace gdb { + using string_view = std::string_view; +} /* namespace gdb */ + +#else /* __cplusplus < 201703L */ + +#include <string> +#include <limits> + +namespace gdb { + + /** + * @class basic_string_view <experimental/string_view> + * @brief A non-owning reference to a string. + * + * @ingroup strings + * @ingroup sequences + * @ingroup experimental + * + * @tparam _CharT Type of character + * @tparam _Traits Traits for character type, defaults to + * char_traits<_CharT>. + * + * A basic_string_view looks like this: + * + * @code + * _CharT* _M_str + * size_t _M_len + * @endcode + */ + template<typename _CharT, typename _Traits = std::char_traits<_CharT>> + class basic_string_view + { + public: + + // types + using traits_type = _Traits; + using value_type = _CharT; + using pointer = const _CharT*; + using const_pointer = const _CharT*; + using reference = const _CharT&; + using const_reference = const _CharT&; + using const_iterator = const _CharT*; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using reverse_iterator = const_reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + static constexpr size_type npos = size_type(-1); + + // [string.view.cons], construct/copy + + constexpr + basic_string_view() noexcept + : _M_len{0}, _M_str{nullptr} + { } + + constexpr basic_string_view(const basic_string_view&) noexcept = default; + + template<typename _Allocator> + basic_string_view(const std::basic_string<_CharT, _Traits, + _Allocator>& __str) noexcept + : _M_len{__str.length()}, _M_str{__str.data()} + { } + + /*constexpr*/ basic_string_view(const _CharT* __str) + : _M_len{__str == nullptr ? 0 : traits_type::length(__str)}, + _M_str{__str} + { } + + constexpr basic_string_view(const _CharT* __str, size_type __len) + : _M_len{__len}, + _M_str{__str} + { } + + basic_string_view& + operator=(const basic_string_view&) noexcept = default; + + // [string.view.iterators], iterators + + constexpr const_iterator + begin() const noexcept + { return this->_M_str; } + + constexpr const_iterator + end() const noexcept + { return this->_M_str + this->_M_len; } + + constexpr const_iterator + cbegin() const noexcept + { return this->_M_str; } + + constexpr const_iterator + cend() const noexcept + { return this->_M_str + this->_M_len; } + + const_reverse_iterator + rbegin() const noexcept + { return const_reverse_iterator(this->end()); } + + const_reverse_iterator + rend() const noexcept + { return const_reverse_iterator(this->begin()); } + + const_reverse_iterator + crbegin() const noexcept + { return const_reverse_iterator(this->end()); } + + const_reverse_iterator + crend() const noexcept + { return const_reverse_iterator(this->begin()); } + + // [string.view.capacity], capacity + + constexpr size_type + size() const noexcept + { return this->_M_len; } + + constexpr size_type + length() const noexcept + { return _M_len; } + + constexpr size_type + max_size() const noexcept + { + return (npos - sizeof(size_type) - sizeof(void*)) + / sizeof(value_type) / 4; + } + + constexpr bool + empty() const noexcept + { return this->_M_len == 0; } + + // [string.view.access], element access + + constexpr const _CharT& + operator[](size_type __pos) const + { + // TODO: Assert to restore in a way compatible with the constexpr. + // __glibcxx_assert(__pos < this->_M_len); + return *(this->_M_str + __pos); + } + + constexpr const _CharT& + at(size_type __pos) const + { + return __pos < this->_M_len + ? *(this->_M_str + __pos) + : (error (_("basic_string_view::at: __pos " + "(which is %zu) >= this->size() " + "(which is %zu)"), + __pos, this->size()), + *this->_M_str); + } + + constexpr const _CharT& + front() const + { + // TODO: Assert to restore in a way compatible with the constexpr. + // __glibcxx_assert(this->_M_len > 0); + return *this->_M_str; + } + + constexpr const _CharT& + back() const + { + // TODO: Assert to restore in a way compatible with the constexpr. + // __glibcxx_assert(this->_M_len > 0); + return *(this->_M_str + this->_M_len - 1); + } + + constexpr const _CharT* + data() const noexcept + { return this->_M_str; } + + // [string.view.modifiers], modifiers: + + /*constexpr*/ void + remove_prefix(size_type __n) + { + gdb_assert (this->_M_len >= __n); + this->_M_str += __n; + this->_M_len -= __n; + } + + /*constexpr*/ void + remove_suffix(size_type __n) + { this->_M_len -= __n; } + + /*constexpr*/ void + swap(basic_string_view& __sv) noexcept + { + auto __tmp = *this; + *this = __sv; + __sv = __tmp; + } + + + // [string.view.ops], string operations: + + template<typename _Allocator> + explicit operator std::basic_string<_CharT, _Traits, _Allocator>() const + { + return { this->_M_str, this->_M_len }; + } + + template<typename _Allocator = std::allocator<_CharT>> + std::basic_string<_CharT, _Traits, _Allocator> + to_string(const _Allocator& __alloc = _Allocator()) const + { + return { this->_M_str, this->_M_len, __alloc }; + } + + size_type + copy(_CharT* __str, size_type __n, size_type __pos = 0) const + { + gdb_assert (__str != nullptr || __n == 0); + if (__pos > this->_M_len) + error (_("basic_string_view::copy: __pos " + "(which is %zu) > this->size() " + "(which is %zu)"), + __pos, this->size()); + size_type __rlen{std::min(__n, size_type{this->_M_len - __pos})}; + for (auto __begin = this->_M_str + __pos, + __end = __begin + __rlen; __begin != __end;) + *__str++ = *__begin++; + return __rlen; + } + + + // [string.view.ops], string operations: + + /*constexpr*/ basic_string_view + substr(size_type __pos, size_type __n=npos) const + { + return __pos <= this->_M_len + ? basic_string_view{this->_M_str + __pos, + std::min(__n, size_type{this->_M_len - __pos})} + : (error (_("basic_string_view::substr: __pos " + "(which is %zu) > this->size() " + "(which is %zu)"), + __pos, this->size()), basic_string_view{}); + } + + /*constexpr*/ int + compare(basic_string_view __str) const noexcept + { + int __ret = traits_type::compare(this->_M_str, __str._M_str, + std::min(this->_M_len, __str._M_len)); + if (__ret == 0) + __ret = _S_compare(this->_M_len, __str._M_len); + return __ret; + } + + /*constexpr*/ int + compare(size_type __pos1, size_type __n1, basic_string_view __str) const + { return this->substr(__pos1, __n1).compare(__str); } + + /*constexpr*/ int + compare(size_type __pos1, size_type __n1, + basic_string_view __str, size_type __pos2, size_type __n2) const + { return this->substr(__pos1, __n1).compare(__str.substr(__pos2, __n2)); } + + /*constexpr*/ int + compare(const _CharT* __str) const noexcept + { return this->compare(basic_string_view{__str}); } + + /*constexpr*/ int + compare(size_type __pos1, size_type __n1, const _CharT* __str) const + { return this->substr(__pos1, __n1).compare(basic_string_view{__str}); } + + /*constexpr*/ int + compare(size_type __pos1, size_type __n1, + const _CharT* __str, size_type __n2) const + { + return this->substr(__pos1, __n1) + .compare(basic_string_view(__str, __n2)); + } + + /*constexpr*/ size_type + find(basic_string_view __str, size_type __pos = 0) const noexcept + { return this->find(__str._M_str, __pos, __str._M_len); } + + /*constexpr*/ size_type + find(_CharT __c, size_type __pos=0) const noexcept; + + /*constexpr*/ size_type + find(const _CharT* __str, size_type __pos, size_type __n) const noexcept; + + /*constexpr*/ size_type + find(const _CharT* __str, size_type __pos=0) const noexcept + { return this->find(__str, __pos, traits_type::length(__str)); } + + /*constexpr*/ size_type + rfind(basic_string_view __str, size_type __pos = npos) const noexcept + { return this->rfind(__str._M_str, __pos, __str._M_len); } + + /*constexpr*/ size_type + rfind(_CharT __c, size_type __pos = npos) const noexcept; + + /*constexpr*/ size_type + rfind(const _CharT* __str, size_type __pos, size_type __n) const noexcept; + + /*constexpr*/ size_type + rfind(const _CharT* __str, size_type __pos = npos) const noexcept + { return this->rfind(__str, __pos, traits_type::length(__str)); } + + /*constexpr*/ size_type + find_first_of(basic_string_view __str, size_type __pos = 0) const noexcept + { return this->find_first_of(__str._M_str, __pos, __str._M_len); } + + /*constexpr*/ size_type + find_first_of(_CharT __c, size_type __pos = 0) const noexcept + { return this->find(__c, __pos); } + + /*constexpr*/ size_type + find_first_of(const _CharT* __str, size_type __pos, size_type __n) const; + + /*constexpr*/ size_type + find_first_of(const _CharT* __str, size_type __pos = 0) const noexcept + { return this->find_first_of(__str, __pos, traits_type::length(__str)); } + + /*constexpr*/ size_type + find_last_of(basic_string_view __str, + size_type __pos = npos) const noexcept + { return this->find_last_of(__str._M_str, __pos, __str._M_len); } + + size_type + find_last_of(_CharT __c, size_type __pos=npos) const noexcept + { return this->rfind(__c, __pos); } + + /*constexpr*/ size_type + find_last_of(const _CharT* __str, size_type __pos, size_type __n) const; + + /*constexpr*/ size_type + find_last_of(const _CharT* __str, size_type __pos = npos) const noexcept + { return this->find_last_of(__str, __pos, traits_type::length(__str)); } + + /*constexpr*/ size_type + find_first_not_of(basic_string_view __str, + size_type __pos = 0) const noexcept + { return this->find_first_not_of(__str._M_str, __pos, __str._M_len); } + + /*constexpr*/ size_type + find_first_not_of(_CharT __c, size_type __pos = 0) const noexcept; + + /*constexpr*/ size_type + find_first_not_of(const _CharT* __str, + size_type __pos, size_type __n) const; + + /*constexpr*/ size_type + find_first_not_of(const _CharT* __str, size_type __pos = 0) const noexcept + { + return this->find_first_not_of(__str, __pos, + traits_type::length(__str)); + } + + /*constexpr*/ size_type + find_last_not_of(basic_string_view __str, + size_type __pos = npos) const noexcept + { return this->find_last_not_of(__str._M_str, __pos, __str._M_len); } + + /*constexpr*/ size_type + find_last_not_of(_CharT __c, size_type __pos = npos) const noexcept; + + /*constexpr*/ size_type + find_last_not_of(const _CharT* __str, + size_type __pos, size_type __n) const; + + /*constexpr*/ size_type + find_last_not_of(const _CharT* __str, + size_type __pos = npos) const noexcept + { + return this->find_last_not_of(__str, __pos, + traits_type::length(__str)); + } + + private: + + static constexpr int + _S_compare(size_type __n1, size_type __n2) noexcept + { + return difference_type(__n1 - __n2) > std::numeric_limits<int>::max() + ? std::numeric_limits<int>::max() + : difference_type(__n1 - __n2) < std::numeric_limits<int>::min() + ? std::numeric_limits<int>::min() + : static_cast<int>(difference_type(__n1 - __n2)); + } + + size_t _M_len; + const _CharT* _M_str; + }; + + // [string.view.comparison], non-member basic_string_view comparison functions + + namespace __detail + { + // Identity transform to create a non-deduced context, so that only one + // argument participates in template argument deduction and the other + // argument gets implicitly converted to the deduced type. See n3766.html. + template<typename _Tp> + using __idt = typename std::common_type<_Tp>::type; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator==(basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.size() == __y.size() && __x.compare(__y) == 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator==(basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return __x.size() == __y.size() && __x.compare(__y) == 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator==(__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.size() == __y.size() && __x.compare(__y) == 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator!=(basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return !(__x == __y); } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator!=(basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return !(__x == __y); } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator!=(__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return !(__x == __y); } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator< (basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) < 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator< (basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return __x.compare(__y) < 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator< (__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) < 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator> (basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) > 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator> (basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return __x.compare(__y) > 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator> (__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) > 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator<=(basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) <= 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator<=(basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return __x.compare(__y) <= 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator<=(__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) <= 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator>=(basic_string_view<_CharT, _Traits> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) >= 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator>=(basic_string_view<_CharT, _Traits> __x, + __detail::__idt<basic_string_view<_CharT, _Traits>> __y) noexcept + { return __x.compare(__y) >= 0; } + + template<typename _CharT, typename _Traits> + /*constexpr*/ bool + operator>=(__detail::__idt<basic_string_view<_CharT, _Traits>> __x, + basic_string_view<_CharT, _Traits> __y) noexcept + { return __x.compare(__y) >= 0; } + + // basic_string_view typedef names + + using string_view = basic_string_view<char>; +} /* namespace gdb */ + +#include "gdb_string_view.tcc" + +#endif // __cplusplus < 201703L + +#endif /* COMMON_GDB_STRING_VIEW_H */ diff --git a/gdb/gdbsupport/gdb_string_view.tcc b/gdb/gdbsupport/gdb_string_view.tcc new file mode 100644 index 00000000000..c30aafab7d6 --- /dev/null +++ b/gdb/gdbsupport/gdb_string_view.tcc @@ -0,0 +1,219 @@ +// Components for manipulating non-owning sequences of characters -*- C++ -*- + +// Note: This file has been stolen from the gcc repo +// (libstdc++-v3/include/experimental/bits/string_view.tcc) and has local +// modifications. + +// Copyright (C) 2013-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +/** @file experimental/bits/string_view.tcc + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{experimental/string_view} + */ + +// +// N3762 basic_string_view library +// + +#ifndef GDB_STRING_VIEW_TCC +#define GDB_STRING_VIEW_TCC 1 + +namespace gdb +{ + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find(const _CharT* __str, size_type __pos, size_type __n) const noexcept + { + gdb_assert (__str != nullptr || __n == 0); + + if (__n == 0) + return __pos <= this->_M_len ? __pos : npos; + + if (__n <= this->_M_len) + { + for (; __pos <= this->_M_len - __n; ++__pos) + if (traits_type::eq(this->_M_str[__pos], __str[0]) + && traits_type::compare(this->_M_str + __pos + 1, + __str + 1, __n - 1) == 0) + return __pos; + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find(_CharT __c, size_type __pos) const noexcept + { + size_type __ret = npos; + if (__pos < this->_M_len) + { + const size_type __n = this->_M_len - __pos; + const _CharT* __p = traits_type::find(this->_M_str + __pos, __n, __c); + if (__p) + __ret = __p - this->_M_str; + } + return __ret; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + rfind(const _CharT* __str, size_type __pos, size_type __n) const noexcept + { + gdb_assert (__str != nullptr || __n == 0); + + if (__n <= this->_M_len) + { + __pos = std::min(size_type(this->_M_len - __n), __pos); + do + { + if (traits_type::compare(this->_M_str + __pos, __str, __n) == 0) + return __pos; + } + while (__pos-- > 0); + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + rfind(_CharT __c, size_type __pos) const noexcept + { + size_type __size = this->_M_len; + if (__size > 0) + { + if (--__size > __pos) + __size = __pos; + for (++__size; __size-- > 0; ) + if (traits_type::eq(this->_M_str[__size], __c)) + return __size; + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_first_of(const _CharT* __str, size_type __pos, size_type __n) const + { + gdb_assert (__str != nullptr || __n == 0); + for (; __n && __pos < this->_M_len; ++__pos) + { + const _CharT* __p = traits_type::find(__str, __n, + this->_M_str[__pos]); + if (__p) + return __pos; + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_last_of(const _CharT* __str, size_type __pos, size_type __n) const + { + gdb_assert (__str != nullptr || __n == 0); + size_type __size = this->size(); + if (__size && __n) + { + if (--__size > __pos) + __size = __pos; + do + { + if (traits_type::find(__str, __n, this->_M_str[__size])) + return __size; + } + while (__size-- != 0); + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_first_not_of(const _CharT* __str, size_type __pos, size_type __n) const + { + gdb_assert (__str != nullptr || __n == 0); + for (; __pos < this->_M_len; ++__pos) + if (!traits_type::find(__str, __n, this->_M_str[__pos])) + return __pos; + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_first_not_of(_CharT __c, size_type __pos) const noexcept + { + for (; __pos < this->_M_len; ++__pos) + if (!traits_type::eq(this->_M_str[__pos], __c)) + return __pos; + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_last_not_of(const _CharT* __str, size_type __pos, size_type __n) const + { + gdb_assert (__str != nullptr || __n == 0); + size_type __size = this->_M_len; + if (__size) + { + if (--__size > __pos) + __size = __pos; + do + { + if (!traits_type::find(__str, __n, this->_M_str[__size])) + return __size; + } + while (__size--); + } + return npos; + } + + template<typename _CharT, typename _Traits> + /*constexpr*/ typename basic_string_view<_CharT, _Traits>::size_type + basic_string_view<_CharT, _Traits>:: + find_last_not_of(_CharT __c, size_type __pos) const noexcept + { + size_type __size = this->_M_len; + if (__size) + { + if (--__size > __pos) + __size = __pos; + do + { + if (!traits_type::eq(this->_M_str[__size], __c)) + return __size; + } + while (__size--); + } + return npos; + } +} // namespace gdb + +#endif // GDB_STRING_VIEW_TCC diff --git a/gdb/gdbsupport/gdb_sys_time.h b/gdb/gdbsupport/gdb_sys_time.h new file mode 100644 index 00000000000..66c2bb355ff --- /dev/null +++ b/gdb/gdbsupport/gdb_sys_time.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2015-2019 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/>. */ + +#ifndef COMMON_GDB_SYS_TIME_H +#define COMMON_GDB_SYS_TIME_H + +#include <sys/time.h> + +/* On MinGW-w64, gnulib's sys/time.h replaces 'struct timeval' and + gettimeofday with versions that support 64-bit time_t, for POSIX + compliance. However, the gettimeofday replacement does not ever + return time_t values larger than 31-bit, as it simply returns the + system's gettimeofday's (signed) 32-bit result as (signed) 64-bit. + Because we don't really need the POSIX compliance, and it ends up + causing conflicts with other libraries we use that don't use gnulib + and thus work with the native struct timeval, such as Winsock2's + native 'select' and libiberty, simply undefine away gnulib's + replacements. */ +#if GNULIB_defined_struct_timeval +# undef timeval +# undef gettimeofday +#endif + +#endif /* COMMON_GDB_SYS_TIME_H */ diff --git a/gdb/gdbsupport/gdb_tilde_expand.c b/gdb/gdbsupport/gdb_tilde_expand.c new file mode 100644 index 00000000000..7e4b06da0e6 --- /dev/null +++ b/gdb/gdbsupport/gdb_tilde_expand.c @@ -0,0 +1,95 @@ +/* Perform tilde expansion on paths for GDB and gdbserver. + + Copyright (C) 2017-2019 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 "gdb_tilde_expand.h" +#include <glob.h> + +/* RAII-style class wrapping "glob". */ + +class gdb_glob +{ +public: + /* Construct a "gdb_glob" object by calling "glob" with the provided + parameters. This function can throw if "glob" fails. */ + gdb_glob (const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno)) + { + int ret = glob (pattern, flags, errfunc, &m_glob); + + if (ret != 0) + { + if (ret == GLOB_NOMATCH) + error (_("Could not find a match for '%s'."), pattern); + else + error (_("glob could not process pattern '%s'."), + pattern); + } + } + + /* Destroy the object and free M_GLOB. */ + ~gdb_glob () + { + globfree (&m_glob); + } + + /* Return the GL_PATHC component of M_GLOB. */ + int pathc () const + { + return m_glob.gl_pathc; + } + + /* Return the GL_PATHV component of M_GLOB. */ + char **pathv () const + { + return m_glob.gl_pathv; + } + +private: + /* The actual glob object we're dealing with. */ + glob_t m_glob; +}; + +/* See gdbsupport/gdb_tilde_expand.h. */ + +std::string +gdb_tilde_expand (const char *dir) +{ + gdb_glob glob (dir, GLOB_TILDE_CHECK, NULL); + + gdb_assert (glob.pathc () > 0); + /* "glob" may return more than one match to the path provided by the + user, but we are only interested in the first match. */ + std::string expanded_dir = glob.pathv ()[0]; + + return expanded_dir; +} + +/* See gdbsupport/gdb_tilde_expand.h. */ + +gdb::unique_xmalloc_ptr<char> +gdb_tilde_expand_up (const char *dir) +{ + gdb_glob glob (dir, GLOB_TILDE_CHECK, NULL); + + gdb_assert (glob.pathc () > 0); + /* "glob" may return more than one match to the path provided by the + user, but we are only interested in the first match. */ + return make_unique_xstrdup (glob.pathv ()[0]); +} diff --git a/gdb/gdbsupport/gdb_tilde_expand.h b/gdb/gdbsupport/gdb_tilde_expand.h new file mode 100644 index 00000000000..b181b6a3e15 --- /dev/null +++ b/gdb/gdbsupport/gdb_tilde_expand.h @@ -0,0 +1,31 @@ +/* Perform tilde expansion on paths for GDB and gdbserver. + + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_GDB_TILDE_EXPAND_H +#define COMMON_GDB_TILDE_EXPAND_H + +/* Perform path expansion (i.e., tilde expansion) on DIR, and return + the full path. */ +extern std::string gdb_tilde_expand (const char *dir); + +/* Same as GDB_TILDE_EXPAND, but return the full path as a + gdb::unique_xmalloc_ptr<char>. */ +extern gdb::unique_xmalloc_ptr<char> gdb_tilde_expand_up (const char *dir); + +#endif /* COMMON_GDB_TILDE_EXPAND_H */ diff --git a/gdb/gdbsupport/gdb_unique_ptr.h b/gdb/gdbsupport/gdb_unique_ptr.h new file mode 100644 index 00000000000..67a5f265353 --- /dev/null +++ b/gdb/gdbsupport/gdb_unique_ptr.h @@ -0,0 +1,67 @@ +/* std::unique_ptr specializations for GDB. + + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_GDB_UNIQUE_PTR_H +#define COMMON_GDB_UNIQUE_PTR_H + +#include <memory> + +namespace gdb +{ +/* Define gdb::unique_xmalloc_ptr, a std::unique_ptr that manages + xmalloc'ed memory. */ + +/* The deleter for std::unique_xmalloc_ptr. Uses xfree. */ +template <typename T> +struct xfree_deleter +{ + void operator() (T *ptr) const { xfree (ptr); } +}; + +/* Same, for arrays. */ +template <typename T> +struct xfree_deleter<T[]> +{ + void operator() (T *ptr) const { xfree (ptr); } +}; + +/* Import the standard unique_ptr to our namespace with a custom + deleter. */ + +template<typename T> using unique_xmalloc_ptr + = std::unique_ptr<T, xfree_deleter<T>>; + +/* A no-op deleter. */ +template<typename T> +struct noop_deleter +{ + void operator() (T *ptr) const { } +}; + +} /* namespace gdb */ + +/* Dup STR and return a unique_xmalloc_ptr for the result. */ + +static inline gdb::unique_xmalloc_ptr<char> +make_unique_xstrdup (const char *str) +{ + return gdb::unique_xmalloc_ptr<char> (xstrdup (str)); +} + +#endif /* COMMON_GDB_UNIQUE_PTR_H */ diff --git a/gdb/gdbsupport/gdb_unlinker.h b/gdb/gdbsupport/gdb_unlinker.h new file mode 100644 index 00000000000..193c2f847c9 --- /dev/null +++ b/gdb/gdbsupport/gdb_unlinker.h @@ -0,0 +1,60 @@ +/* Unlinking class + + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_GDB_UNLINKER_H +#define COMMON_GDB_UNLINKER_H + +namespace gdb +{ + +/* An object of this class holds a filename and, when the object goes + of scope, the file is removed using unlink. + + A user of this class can request that the file be preserved using + the "keep" method. */ +class unlinker +{ + public: + + unlinker (const char *filename) ATTRIBUTE_NONNULL (2) + : m_filename (filename) + { + gdb_assert (filename != NULL); + } + + ~unlinker () + { + if (m_filename != NULL) + unlink (m_filename); + } + + /* Keep the file, rather than unlink it. */ + void keep () + { + m_filename = NULL; + } + + private: + + const char *m_filename; +}; + +} + +#endif /* COMMON_GDB_UNLINKER_H */ diff --git a/gdb/gdbsupport/gdb_vecs.c b/gdb/gdbsupport/gdb_vecs.c new file mode 100644 index 00000000000..38f42f54c9a --- /dev/null +++ b/gdb/gdbsupport/gdb_vecs.c @@ -0,0 +1,88 @@ +/* Some commonly-used VEC types. + + Copyright (C) 2012-2019 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 "gdb_vecs.h" +#include "host-defs.h" + +/* Worker function to split character delimiter separated string of fields + STR into a char pointer vector. */ + +static void +delim_string_to_char_ptr_vec_append + (std::vector<gdb::unique_xmalloc_ptr<char>> *vecp, const char *str, + char delimiter) +{ + do + { + size_t this_len; + const char *next_field; + char *this_field; + + next_field = strchr (str, delimiter); + if (next_field == NULL) + this_len = strlen (str); + else + { + this_len = next_field - str; + next_field++; + } + + this_field = (char *) xmalloc (this_len + 1); + memcpy (this_field, str, this_len); + this_field[this_len] = '\0'; + vecp->emplace_back (this_field); + + str = next_field; + } + while (str != NULL); +} + +/* See gdb_vecs.h. */ + +std::vector<gdb::unique_xmalloc_ptr<char>> +delim_string_to_char_ptr_vec (const char *str, char delimiter) +{ + std::vector<gdb::unique_xmalloc_ptr<char>> retval; + + delim_string_to_char_ptr_vec_append (&retval, str, delimiter); + + return retval; +} + +/* See gdb_vecs.h. */ + +void +dirnames_to_char_ptr_vec_append + (std::vector<gdb::unique_xmalloc_ptr<char>> *vecp, const char *dirnames) +{ + delim_string_to_char_ptr_vec_append (vecp, dirnames, DIRNAME_SEPARATOR); +} + +/* See gdb_vecs.h. */ + +std::vector<gdb::unique_xmalloc_ptr<char>> +dirnames_to_char_ptr_vec (const char *dirnames) +{ + std::vector<gdb::unique_xmalloc_ptr<char>> retval; + + dirnames_to_char_ptr_vec_append (&retval, dirnames); + + return retval; +} diff --git a/gdb/gdbsupport/gdb_vecs.h b/gdb/gdbsupport/gdb_vecs.h new file mode 100644 index 00000000000..017bf9383dc --- /dev/null +++ b/gdb/gdbsupport/gdb_vecs.h @@ -0,0 +1,89 @@ +/* Some commonly-used VEC types. + + Copyright (C) 2012-2019 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/>. */ + +#ifndef COMMON_GDB_VECS_H +#define COMMON_GDB_VECS_H + +#include "vec.h" + +/* Split STR, a list of DELIMITER-separated fields, into a char pointer vector. + + You may modify the returned strings. */ + +extern std::vector<gdb::unique_xmalloc_ptr<char>> + delim_string_to_char_ptr_vec (const char *str, char delimiter); + +/* Like dirnames_to_char_ptr_vec, but append the directories to *VECP. */ + +extern void dirnames_to_char_ptr_vec_append + (std::vector<gdb::unique_xmalloc_ptr<char>> *vecp, const char *dirnames); + +/* Split DIRNAMES by DIRNAME_SEPARATOR delimiter and return a list of all the + elements in their original order. For empty string ("") DIRNAMES return + list of one empty string ("") element. + + You may modify the returned strings. */ + +extern std::vector<gdb::unique_xmalloc_ptr<char>> + dirnames_to_char_ptr_vec (const char *dirnames); + +/* Remove the element pointed by iterator IT from VEC, not preserving the order + of the remaining elements. Return the removed element. */ + +template <typename T> +T +unordered_remove (std::vector<T> &vec, typename std::vector<T>::iterator it) +{ + gdb_assert (it >= vec.begin () && it < vec.end ()); + + T removed = std::move (*it); + *it = std::move (vec.back ()); + vec.pop_back (); + + return removed; +} + +/* Remove the element at position IX from VEC, not preserving the order of the + remaining elements. Return the removed element. */ + +template <typename T> +T +unordered_remove (std::vector<T> &vec, typename std::vector<T>::size_type ix) +{ + gdb_assert (ix < vec.size ()); + + return unordered_remove (vec, vec.begin () + ix); +} + +/* Remove the element at position IX from VEC, preserving the order the + remaining elements. Return the removed element. */ + +template <typename T> +T +ordered_remove (std::vector<T> &vec, typename std::vector<T>::size_type ix) +{ + gdb_assert (ix < vec.size ()); + + T removed = std::move (vec[ix]); + vec.erase (vec.begin () + ix); + + return removed; +} + +#endif /* COMMON_GDB_VECS_H */ diff --git a/gdb/gdbsupport/gdb_wait.h b/gdb/gdbsupport/gdb_wait.h new file mode 100644 index 00000000000..b3b752cf3a2 --- /dev/null +++ b/gdb/gdbsupport/gdb_wait.h @@ -0,0 +1,113 @@ +/* Standard wait macros. + Copyright (C) 2000-2019 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/>. */ + +#ifndef COMMON_GDB_WAIT_H +#define COMMON_GDB_WAIT_H + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> /* POSIX */ +#else +#ifdef HAVE_WAIT_H +#include <wait.h> /* legacy */ +#endif +#endif + +/* Define how to access the int that the wait system call stores. + This has been compatible in all Unix systems since time immemorial, + but various well-meaning people have defined various different + words for the same old bits in the same old int (sometimes claimed + to be a struct). We just know it's an int and we use these macros + to access the bits. */ + +/* The following macros are defined equivalently to their definitions + in POSIX.1. We fail to define WNOHANG and WUNTRACED, which POSIX.1 + <sys/wait.h> defines, since our code does not use waitpid() (but + NOTE exception for GNU/Linux below). We also fail to declare + wait() and waitpid(). */ + +#ifndef WIFEXITED +#define WIFEXITED(w) (((w)&0377) == 0) +#endif + +#ifndef WIFSIGNALED +#define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0) +#endif + +#ifndef WIFSTOPPED +#ifdef IBM6000 + +/* Unfortunately, the above comment (about being compatible in all Unix + systems) is not quite correct for AIX, sigh. And AIX 3.2 can generate + status words like 0x57c (sigtrap received after load), and gdb would + choke on it. */ + +#define WIFSTOPPED(w) ((w)&0x40) + +#else +#define WIFSTOPPED(w) (((w)&0377) == 0177) +#endif +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(w) (((w) >> 8) & 0377) /* same as WRETCODE */ +#endif + +#ifndef WTERMSIG +#define WTERMSIG(w) ((w) & 0177) +#endif + +#ifndef WSTOPSIG +#define WSTOPSIG WEXITSTATUS +#endif + +/* These are not defined in POSIX, but are used by our programs. */ + +#ifndef WSETEXIT +# ifdef W_EXITCODE +#define WSETEXIT(w,status) ((w) = W_EXITCODE(status,0)) +# else +#define WSETEXIT(w,status) ((w) = (0 | ((status) << 8))) +# endif +#endif + +#ifndef W_STOPCODE +#define W_STOPCODE(sig) ((sig) << 8 | 0x7f) +#endif + +#ifndef WSETSTOP +#define WSETSTOP(w,sig) ((w) = W_STOPCODE(sig)) +#endif + +/* For native GNU/Linux we may use waitpid and the __WCLONE option. + <GRIPE> It is of course dangerous not to use the REAL header file... + </GRIPE>. */ + +/* Bits in the third argument to `waitpid'. */ +#ifndef WNOHANG +#define WNOHANG 1 /* Don't block waiting. */ +#endif + +#ifndef WUNTRACED +#define WUNTRACED 2 /* Report status of stopped children. */ +#endif + +#ifndef __WCLONE +#define __WCLONE 0x80000000 /* Wait for cloned process. */ +#endif + +#endif /* COMMON_GDB_WAIT_H */ diff --git a/gdb/gdbsupport/hash_enum.h b/gdb/gdbsupport/hash_enum.h new file mode 100644 index 00000000000..4baf01eb9ca --- /dev/null +++ b/gdb/gdbsupport/hash_enum.h @@ -0,0 +1,45 @@ +/* A hasher for enums. + + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_HASH_ENUM_H +#define COMMON_HASH_ENUM_H + +/* A hasher for enums, which was missing in C++11: + http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148 +*/ + +namespace gdb { + +/* Helper struct for hashing enum types. */ +template<typename T> +struct hash_enum +{ + typedef size_t result_type; + typedef T argument_type; + + size_t operator() (T val) const noexcept + { + using underlying = typename std::underlying_type<T>::type; + return std::hash<underlying> () (static_cast<underlying> (val)); + } +}; + +} /* namespace gdb */ + +#endif /* COMMON_HASH_ENUM_H */ diff --git a/gdb/gdbsupport/host-defs.h b/gdb/gdbsupport/host-defs.h new file mode 100644 index 00000000000..dedd9b6fa99 --- /dev/null +++ b/gdb/gdbsupport/host-defs.h @@ -0,0 +1,61 @@ +/* Basic host-specific definitions for GDB. + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_HOST_DEFS_H +#define COMMON_HOST_DEFS_H + +#include <limits.h> + +/* Static host-system-dependent parameters for GDB. */ + +/* * Number of bits in a char or unsigned char for the target machine. + Just like CHAR_BIT in <limits.h> but describes the target machine. */ +#if !defined (TARGET_CHAR_BIT) +#define TARGET_CHAR_BIT 8 +#endif + +/* * If we picked up a copy of CHAR_BIT from a configuration file + (which may get it by including <limits.h>) then use it to set + the number of bits in a host char. If not, use the same size + as the target. */ + +#if defined (CHAR_BIT) +#define HOST_CHAR_BIT CHAR_BIT +#else +#define HOST_CHAR_BIT TARGET_CHAR_BIT +#endif + +#ifdef __MSDOS__ +# define CANT_FORK +# define GLOBAL_CURDIR +# define DIRNAME_SEPARATOR ';' +#endif + +#if !defined (__CYGWIN__) && defined (_WIN32) +# define DIRNAME_SEPARATOR ';' +#endif + +#ifndef DIRNAME_SEPARATOR +#define DIRNAME_SEPARATOR ':' +#endif + +#ifndef SLASH_STRING +#define SLASH_STRING "/" +#endif + +#endif /* COMMON_HOST_DEFS_H */ diff --git a/gdb/gdbsupport/job-control.c b/gdb/gdbsupport/job-control.c new file mode 100644 index 00000000000..cd3dde12fbf --- /dev/null +++ b/gdb/gdbsupport/job-control.c @@ -0,0 +1,86 @@ +/* Job control and terminal related functions, for GDB and gdbserver + when running under Unix. + + Copyright (C) 1986-2019 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 "job-control.h" +#ifdef HAVE_TERMIOS_H +#include <termios.h> +#endif +#include <unistd.h> + +/* Nonzero if we have job control. */ +int job_control; + +/* Set the process group ID of the inferior. + + Just using job_control only does part of it because setpgid or + setpgrp might not exist on a system without job control. + + For a more clean implementation, in libiberty, put a setpgid which merely + calls setpgrp and a setpgrp which does nothing (any system with job control + will have one or the other). */ + +int +gdb_setpgid () +{ + int retval = 0; + + if (job_control) + { +#ifdef HAVE_SETPGID + /* The call setpgid (0, 0) is supposed to work and mean the same + thing as this, but on Ultrix 4.2A it fails with EPERM (and + setpgid (getpid (), getpid ()) succeeds). */ + retval = setpgid (getpid (), getpid ()); +#else +#ifdef HAVE_SETPGRP +#ifdef SETPGRP_VOID + retval = setpgrp (); +#else + retval = setpgrp (getpid (), getpid ()); +#endif +#endif /* HAVE_SETPGRP */ +#endif /* HAVE_SETPGID */ + } + + return retval; +} + +/* See gdbsupport/common-terminal.h. */ + +void +have_job_control () +{ + /* OK, figure out whether we have job control. If termios is not + available, leave job_control 0. */ +#if defined (HAVE_TERMIOS_H) + /* Do all systems with termios have the POSIX way of identifying job + control? I hope so. */ +#ifdef _POSIX_JOB_CONTROL + job_control = 1; +#else +#ifdef _SC_JOB_CONTROL + job_control = sysconf (_SC_JOB_CONTROL); +#else + job_control = 0; /* Have to assume the worst. */ +#endif /* _SC_JOB_CONTROL */ +#endif /* _POSIX_JOB_CONTROL */ +#endif /* HAVE_TERMIOS_H */ +} diff --git a/gdb/gdbsupport/job-control.h b/gdb/gdbsupport/job-control.h new file mode 100644 index 00000000000..e4660ae50cb --- /dev/null +++ b/gdb/gdbsupport/job-control.h @@ -0,0 +1,38 @@ +/* Job control and terminal related functions, for GDB and gdbserver + when running under Unix. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_JOB_CONTROL_H +#define COMMON_JOB_CONTROL_H + +/* Do we have job control? Can be assumed to always be the same + within a given run of GDB. Use in gdb/inflow.c and + gdbsupport/common-inflow.c. */ +extern int job_control; + +/* Set the process group of the caller to its own pid, or do nothing + if we lack job control. */ +extern int gdb_setpgid (); + +/* Determine whether we have job control, and set variable JOB_CONTROL + accordingly. This function must be called before any use of + JOB_CONTROL. */ +extern void have_job_control (); + +#endif /* COMMON_JOB_CONTROL_H */ diff --git a/gdb/gdbsupport/mingw-strerror.c b/gdb/gdbsupport/mingw-strerror.c new file mode 100644 index 00000000000..6386330062c --- /dev/null +++ b/gdb/gdbsupport/mingw-strerror.c @@ -0,0 +1,64 @@ +/* Safe version of strerror for MinGW, for GDB, the GNU debugger. + + Copyright (C) 2006-2019 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 <windows.h> + +/* Implementation of safe_strerror as defined in common-utils.h. + + The Windows runtime implementation of strerror never returns NULL, + but does return a useless string for anything above sys_nerr; + unfortunately this includes all socket-related error codes. + This replacement tries to find a system-provided error message. */ + +char * +safe_strerror (int errnum) +{ + static char *buffer; + int len; + + if (errnum >= 0 && errnum < sys_nerr) + return strerror (errnum); + + if (buffer) + { + LocalFree (buffer); + buffer = NULL; + } + + if (FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, errnum, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &buffer, 0, NULL) == 0) + { + static char buf[32]; + xsnprintf (buf, sizeof buf, "(undocumented errno %d)", errnum); + return buf; + } + + /* Windows error messages end with a period and a CR-LF; strip that + out. */ + len = strlen (buffer); + if (len > 3 && strcmp (buffer + len - 3, ".\r\n") == 0) + buffer[len - 3] = '\0'; + + return buffer; +} diff --git a/gdb/gdbsupport/netstuff.c b/gdb/gdbsupport/netstuff.c new file mode 100644 index 00000000000..a7dc3465913 --- /dev/null +++ b/gdb/gdbsupport/netstuff.c @@ -0,0 +1,154 @@ +/* Operations on network stuff. + Copyright (C) 2018-2019 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 "netstuff.h" +#include <algorithm> + +#ifdef USE_WIN32API +#include <ws2tcpip.h> +#else +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/tcp.h> +#endif + +/* See gdbsupport/netstuff.h. */ + +scoped_free_addrinfo::~scoped_free_addrinfo () +{ + freeaddrinfo (m_res); +} + +/* See gdbsupport/netstuff.h. */ + +parsed_connection_spec +parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint) +{ + parsed_connection_spec ret; + size_t last_colon_pos = 0; + /* We're dealing with IPv6 if: + + - ai_family is AF_INET6, or + - ai_family is not AF_INET, and + - spec[0] is '[', or + - the number of ':' on spec is greater than 1. */ + bool is_ipv6 = (hint->ai_family == AF_INET6 + || (hint->ai_family != AF_INET + && (spec[0] == '[' + || std::count (spec.begin (), + spec.end (), ':') > 1))); + + if (is_ipv6) + { + if (spec[0] == '[') + { + /* IPv6 addresses can be written as '[ADDR]:PORT', and we + support this notation. */ + size_t close_bracket_pos = spec.find_first_of (']'); + + if (close_bracket_pos == std::string::npos) + error (_("Missing close bracket in hostname '%s'"), + spec.c_str ()); + + hint->ai_family = AF_INET6; + + const char c = spec[close_bracket_pos + 1]; + + if (c == '\0') + last_colon_pos = std::string::npos; + else if (c != ':') + error (_("Invalid cruft after close bracket in '%s'"), + spec.c_str ()); + + /* Erase both '[' and ']'. */ + spec.erase (0, 1); + spec.erase (close_bracket_pos - 1, 1); + } + else if (spec.find_first_of (']') != std::string::npos) + error (_("Missing open bracket in hostname '%s'"), + spec.c_str ()); + } + + if (last_colon_pos == 0) + last_colon_pos = spec.find_last_of (':'); + + /* The length of the hostname part. */ + size_t host_len; + + if (last_colon_pos != std::string::npos) + { + /* The user has provided a port. */ + host_len = last_colon_pos; + ret.port_str = spec.substr (last_colon_pos + 1); + } + else + host_len = spec.size (); + + ret.host_str = spec.substr (0, host_len); + + /* Default hostname is localhost. */ + if (ret.host_str.empty ()) + ret.host_str = "localhost"; + + return ret; +} + +/* See gdbsupport/netstuff.h. */ + +parsed_connection_spec +parse_connection_spec (const char *spec, struct addrinfo *hint) +{ + /* Struct to hold the association between valid prefixes, their + family and socktype. */ + struct host_prefix + { + /* The prefix. */ + const char *prefix; + + /* The 'ai_family'. */ + int family; + + /* The 'ai_socktype'. */ + int socktype; + }; + static const struct host_prefix prefixes[] = + { + { "udp:", AF_UNSPEC, SOCK_DGRAM }, + { "tcp:", AF_UNSPEC, SOCK_STREAM }, + { "udp4:", AF_INET, SOCK_DGRAM }, + { "tcp4:", AF_INET, SOCK_STREAM }, + { "udp6:", AF_INET6, SOCK_DGRAM }, + { "tcp6:", AF_INET6, SOCK_STREAM }, + }; + + for (const host_prefix prefix : prefixes) + if (startswith (spec, prefix.prefix)) + { + spec += strlen (prefix.prefix); + hint->ai_family = prefix.family; + hint->ai_socktype = prefix.socktype; + hint->ai_protocol + = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; + break; + } + + return parse_connection_spec_without_prefix (spec, hint); +} diff --git a/gdb/gdbsupport/netstuff.h b/gdb/gdbsupport/netstuff.h new file mode 100644 index 00000000000..0d39fb5eea8 --- /dev/null +++ b/gdb/gdbsupport/netstuff.h @@ -0,0 +1,76 @@ +/* Operations on network stuff. + Copyright (C) 2018-2019 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/>. */ + +#ifndef COMMON_NETSTUFF_H +#define COMMON_NETSTUFF_H + +#include <string> + +/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms. */ +#define GDB_NI_MAX_ADDR 64 +#define GDB_NI_MAX_PORT 16 + +/* Helper class to guarantee that we always call 'freeaddrinfo'. */ + +class scoped_free_addrinfo +{ +public: + /* Default constructor. */ + explicit scoped_free_addrinfo (struct addrinfo *ainfo) + : m_res (ainfo) + { + } + + /* Destructor responsible for free'ing M_RES by calling + 'freeaddrinfo'. */ + ~scoped_free_addrinfo (); + + DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo); + +private: + /* The addrinfo resource. */ + struct addrinfo *m_res; +}; + +/* The struct we return after parsing the connection spec. */ + +struct parsed_connection_spec +{ + /* The hostname. */ + std::string host_str; + + /* The port, if any. */ + std::string port_str; +}; + + +/* Parse SPEC (which is a string in the form of "ADDR:PORT") and + return a 'parsed_connection_spec' structure with the proper fields + filled in. Also adjust HINT accordingly. */ +extern parsed_connection_spec + parse_connection_spec_without_prefix (std::string spec, + struct addrinfo *hint); + +/* Parse SPEC (which is a string in the form of + "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_connection_spec' + structure with the proper fields filled in. Also adjust HINT + accordingly. */ +extern parsed_connection_spec parse_connection_spec (const char *spec, + struct addrinfo *hint); + +#endif /* COMMON_NETSTUFF_H */ diff --git a/gdb/gdbsupport/new-op.c b/gdb/gdbsupport/new-op.c new file mode 100644 index 00000000000..7c5dba0be6d --- /dev/null +++ b/gdb/gdbsupport/new-op.c @@ -0,0 +1,95 @@ +/* Replace operator new/new[], for GDB, the GNU debugger. + + Copyright (C) 2016-2019 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/>. */ + +/* GCC does not understand __has_feature. */ +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +#if !__has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) +#include "common-defs.h" +#include "host-defs.h" +#include <new> + +/* Override operator new / operator new[], in order to internal_error + on allocation failure and thus query the user for abort/core + dump/continue, just like xmalloc does. We don't do this from a + new-handler function instead (std::set_new_handler) because we want + to catch allocation errors from within global constructors too. + + Skip overriding if building with -fsanitize=address though. + Address sanitizer wants to override operator new/delete too in + order to detect malloc+delete and new+free mismatches. Our + versions would mask out ASan's, with the result of losing that + useful mismatch detection. + + Note that C++ implementations could either have their throw + versions call the nothrow versions (libstdc++), or the other way + around (clang/libc++). For that reason, we replace both throw and + nothrow variants and call malloc directly. */ + +void * +operator new (std::size_t sz) +{ + /* malloc (0) is unpredictable; avoid it. */ + if (sz == 0) + sz = 1; + + void *p = malloc (sz); /* ARI: malloc */ + if (p == NULL) + { + /* If the user decides to continue debugging, throw a + gdb_quit_bad_alloc exception instead of a regular QUIT + gdb_exception. The former extends both std::bad_alloc and a + QUIT gdb_exception. This is necessary because operator new + can only ever throw std::bad_alloc, or something that extends + it. */ + try + { + malloc_failure (sz); + } + catch (gdb_exception &ex) + { + throw gdb_quit_bad_alloc (std::move (ex)); + } + } + return p; +} + +void * +operator new (std::size_t sz, const std::nothrow_t&) noexcept +{ + /* malloc (0) is unpredictable; avoid it. */ + if (sz == 0) + sz = 1; + return malloc (sz); /* ARI: malloc */ +} + +void * +operator new[] (std::size_t sz) +{ + return ::operator new (sz); +} + +void* +operator new[] (std::size_t sz, const std::nothrow_t&) noexcept +{ + return ::operator new (sz, std::nothrow); +} +#endif diff --git a/gdb/gdbsupport/next-iterator.h b/gdb/gdbsupport/next-iterator.h new file mode 100644 index 00000000000..b61b253bd9d --- /dev/null +++ b/gdb/gdbsupport/next-iterator.h @@ -0,0 +1,102 @@ +/* A "next" iterator for GDB, the GNU debugger. + Copyright (C) 2019 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/>. */ + +#ifndef COMMON_NEXT_ITERATOR_H +#define COMMON_NEXT_ITERATOR_H + +/* An iterator that uses the 'next' field of a type to iterate. This + can be used with various GDB types that are stored as linked + lists. */ + +template<typename T> +struct next_iterator +{ + typedef next_iterator self_type; + typedef T *value_type; + typedef T *&reference; + typedef T **pointer; + typedef std::forward_iterator_tag iterator_category; + typedef int difference_type; + + explicit next_iterator (T *item) + : m_item (item) + { + } + + /* Create a one-past-the-end iterator. */ + next_iterator () + : m_item (nullptr) + { + } + + value_type operator* () const + { + return m_item; + } + + bool operator== (const self_type &other) const + { + return m_item == other.m_item; + } + + bool operator!= (const self_type &other) const + { + return m_item != other.m_item; + } + + self_type &operator++ () + { + m_item = m_item->next; + return *this; + } + +private: + + T *m_item; +}; + +/* A range adapter that allows iterating over a linked list. */ + +template<typename T, typename Iterator = next_iterator<T>> +class next_adapter +{ +public: + + explicit next_adapter (T *item) + : m_item (item) + { + } + + using iterator = Iterator; + + iterator begin () const + { + return iterator (m_item); + } + + iterator end () const + { + return iterator (); + } + +private: + + T *m_item; +}; + +#endif /* COMMON_NEXT_ITERATOR_H */ diff --git a/gdb/gdbsupport/observable.h b/gdb/gdbsupport/observable.h new file mode 100644 index 00000000000..205c30382c6 --- /dev/null +++ b/gdb/gdbsupport/observable.h @@ -0,0 +1,119 @@ +/* Observers + + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_OBSERVABLE_H +#define COMMON_OBSERVABLE_H + +#include <algorithm> +#include <functional> +#include <vector> + +namespace gdb +{ + +namespace observers +{ + +extern unsigned int observer_debug; + +/* An observer is an entity which is interested in being notified + when GDB reaches certain states, or certain events occur in GDB. + The entity being observed is called the observable. To receive + notifications, the observer attaches a callback to the observable. + One observable can have several observers. + + The observer implementation is also currently not reentrant. In + particular, it is therefore not possible to call the attach or + detach routines during a notification. */ + +/* The type of a key that can be passed to attach, which can be passed + to detach to remove associated observers. Tokens have address + identity, and are thus usually const globals. */ +struct token +{ + token () = default; + + DISABLE_COPY_AND_ASSIGN (token); +}; + +template<typename... T> +class observable +{ +public: + + typedef std::function<void (T...)> func_type; + + explicit observable (const char *name) + : m_name (name) + { + } + + DISABLE_COPY_AND_ASSIGN (observable); + + /* Attach F as an observer to this observable. F cannot be + detached. */ + void attach (const func_type &f) + { + m_observers.emplace_back (nullptr, f); + } + + /* Attach F as an observer to this observable. T is a reference to + a token that can be used to later remove F. */ + void attach (const func_type &f, const token &t) + { + m_observers.emplace_back (&t, f); + } + + /* Remove observers associated with T from this observable. T is + the token that was previously passed to any number of "attach" + calls. */ + void detach (const token &t) + { + auto iter = std::remove_if (m_observers.begin (), + m_observers.end (), + [&] (const std::pair<const token *, + func_type> &e) + { + return e.first == &t; + }); + + m_observers.erase (iter, m_observers.end ()); + } + + /* Notify all observers that are attached to this observable. */ + void notify (T... args) const + { + if (observer_debug) + fprintf_unfiltered (gdb_stdlog, "observable %s notify() called\n", + m_name); + for (auto &&e : m_observers) + e.second (args...); + } + +private: + + std::vector<std::pair<const token *, func_type>> m_observers; + const char *m_name; +}; + +} /* namespace observers */ + +} /* namespace gdb */ + +#endif /* COMMON_OBSERVABLE_H */ diff --git a/gdb/gdbsupport/offset-type.h b/gdb/gdbsupport/offset-type.h new file mode 100644 index 00000000000..e1495b28017 --- /dev/null +++ b/gdb/gdbsupport/offset-type.h @@ -0,0 +1,133 @@ +/* Offset types for GDB. + + Copyright (C) 2017-2019 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/>. */ + +/* Define an "offset" type. Offset types are distinct integer types + that are used to represent an offset into anything that is + addressable. For example, an offset into a DWARF debug section. + The idea is catch mixing unrelated offset types at compile time, in + code that needs to manipulate multiple different kinds of offsets + that are easily confused. They're safer to use than native + integers, because they have no implicit conversion to anything. + And also, since they're implemented as "enum class" strong + typedefs, they're still integers ABI-wise, making them a bit more + efficient than wrapper structs on some ABIs. + + Some properties of offset types, loosely modeled on pointers: + + - You can compare offsets of the same type for equality and order. + You can't compare an offset with an unrelated type. + + - You can add/substract an integer to/from an offset, which gives + you back a shifted offset. + + - You can subtract two offsets of the same type, which gives you + back the delta as an integer (of the enum class's underlying + type), not as an offset type. + + - You can't add two offsets of the same type, as that would not + make sense. + + However, unlike pointers, you can't deference offset types. */ + +#ifndef COMMON_OFFSET_TYPE_H +#define COMMON_OFFSET_TYPE_H + +/* Declare TYPE as being an offset type. This declares the type and + enables the operators defined below. */ +#define DEFINE_OFFSET_TYPE(TYPE, UNDERLYING) \ + enum class TYPE : UNDERLYING {}; \ + void is_offset_type (TYPE) + +/* The macro macro is all you need to know use offset types. The rest + below is all implementation detail. */ + +/* For each enum class type that you want to support arithmetic + operators, declare an "is_offset_type" overload that has exactly + one parameter, of type that enum class. E.g.,: + + void is_offset_type (sect_offset); + + The function does not need to be defined, only declared. + DEFINE_OFFSET_TYPE declares this. + + A function declaration is preferred over a traits type, because the + former allows calling the DEFINE_OFFSET_TYPE macro inside a + namespace to define the corresponding offset type in that + namespace. The compiler finds the corresponding is_offset_type + function via ADL. +*/ + +/* Adding or subtracting an integer to an offset type shifts the + offset. This is like "PTR = PTR + INT" and "PTR += INT". */ + +#define DEFINE_OFFSET_ARITHM_OP(OP) \ + template<typename E, \ + typename = decltype (is_offset_type (std::declval<E> ()))> \ + constexpr E \ + operator OP (E lhs, typename std::underlying_type<E>::type rhs) \ + { \ + using underlying = typename std::underlying_type<E>::type; \ + return (E) (static_cast<underlying> (lhs) OP rhs); \ + } \ + \ + template<typename E, \ + typename = decltype (is_offset_type (std::declval<E> ()))> \ + constexpr E \ + operator OP (typename std::underlying_type<E>::type lhs, E rhs) \ + { \ + using underlying = typename std::underlying_type<E>::type; \ + return (E) (lhs OP static_cast<underlying> (rhs)); \ + } \ + \ + template<typename E, \ + typename = decltype (is_offset_type (std::declval<E> ()))> \ + E & \ + operator OP ## = (E &lhs, typename std::underlying_type<E>::type rhs) \ + { \ + using underlying = typename std::underlying_type<E>::type; \ + lhs = (E) (static_cast<underlying> (lhs) OP rhs); \ + return lhs; \ + } + +DEFINE_OFFSET_ARITHM_OP(+) +DEFINE_OFFSET_ARITHM_OP(-) + +/* Adding two offset types doesn't make sense, just like "PTR + PTR" + doesn't make sense. This is defined as a deleted function so that + a compile error easily brings you to this comment. */ + +template<typename E, + typename = decltype (is_offset_type (std::declval<E> ()))> +constexpr typename std::underlying_type<E>::type +operator+ (E lhs, E rhs) = delete; + +/* Subtracting two offset types, however, gives you back the + difference between the offsets, as an underlying type. Similar to + how "PTR2 - PTR1" returns a ptrdiff_t. */ + +template<typename E, + typename = decltype (is_offset_type (std::declval<E> ()))> +constexpr typename std::underlying_type<E>::type +operator- (E lhs, E rhs) +{ + using underlying = typename std::underlying_type<E>::type; + return static_cast<underlying> (lhs) - static_cast<underlying> (rhs); +} + +#endif /* COMMON_OFFSET_TYPE_H */ diff --git a/gdb/gdbsupport/pathstuff.c b/gdb/gdbsupport/pathstuff.c new file mode 100644 index 00000000000..fafecd543d3 --- /dev/null +++ b/gdb/gdbsupport/pathstuff.c @@ -0,0 +1,290 @@ +/* Path manipulation routines for GDB and gdbserver. + + Copyright (C) 1986-2019 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 "pathstuff.h" +#include "host-defs.h" +#include "filenames.h" +#include "gdb_tilde_expand.h" + +#ifdef USE_WIN32API +#include <windows.h> +#endif + +/* See gdbsupport/pathstuff.h. */ + +gdb::unique_xmalloc_ptr<char> +gdb_realpath (const char *filename) +{ +/* On most hosts, we rely on canonicalize_file_name to compute + the FILENAME's realpath. + + But the situation is slightly more complex on Windows, due to some + versions of GCC which were reported to generate paths where + backlashes (the directory separator) were doubled. For instance: + c:\\some\\double\\slashes\\dir + ... instead of ... + c:\some\double\slashes\dir + Those double-slashes were getting in the way when comparing paths, + for instance when trying to insert a breakpoint as follow: + (gdb) b c:/some/double/slashes/dir/foo.c:4 + No source file named c:/some/double/slashes/dir/foo.c:4. + (gdb) b c:\some\double\slashes\dir\foo.c:4 + No source file named c:\some\double\slashes\dir\foo.c:4. + To prevent this from happening, we need this function to always + strip those extra backslashes. While canonicalize_file_name does + perform this simplification, it only works when the path is valid. + Since the simplification would be useful even if the path is not + valid (one can always set a breakpoint on a file, even if the file + does not exist locally), we rely instead on GetFullPathName to + perform the canonicalization. */ + +#if defined (_WIN32) + { + char buf[MAX_PATH]; + DWORD len = GetFullPathName (filename, MAX_PATH, buf, NULL); + + /* The file system is case-insensitive but case-preserving. + So it is important we do not lowercase the path. Otherwise, + we might not be able to display the original casing in a given + path. */ + if (len > 0 && len < MAX_PATH) + return make_unique_xstrdup (buf); + } +#else + { + char *rp = canonicalize_file_name (filename); + + if (rp != NULL) + return gdb::unique_xmalloc_ptr<char> (rp); + } +#endif + + /* This system is a lost cause, just dup the buffer. */ + return make_unique_xstrdup (filename); +} + +/* See gdbsupport/pathstuff.h. */ + +gdb::unique_xmalloc_ptr<char> +gdb_realpath_keepfile (const char *filename) +{ + const char *base_name = lbasename (filename); + char *dir_name; + char *result; + + /* Extract the basename of filename, and return immediately + a copy of filename if it does not contain any directory prefix. */ + if (base_name == filename) + return make_unique_xstrdup (filename); + + dir_name = (char *) alloca ((size_t) (base_name - filename + 2)); + /* Allocate enough space to store the dir_name + plus one extra + character sometimes needed under Windows (see below), and + then the closing \000 character. */ + strncpy (dir_name, filename, base_name - filename); + dir_name[base_name - filename] = '\000'; + +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + /* We need to be careful when filename is of the form 'd:foo', which + is equivalent of d:./foo, which is totally different from d:/foo. */ + if (strlen (dir_name) == 2 && isalpha (dir_name[0]) && dir_name[1] == ':') + { + dir_name[2] = '.'; + dir_name[3] = '\000'; + } +#endif + + /* Canonicalize the directory prefix, and build the resulting + filename. If the dirname realpath already contains an ending + directory separator, avoid doubling it. */ + gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name); + const char *real_path = path_storage.get (); + if (IS_DIR_SEPARATOR (real_path[strlen (real_path) - 1])) + result = concat (real_path, base_name, (char *) NULL); + else + result = concat (real_path, SLASH_STRING, base_name, (char *) NULL); + + return gdb::unique_xmalloc_ptr<char> (result); +} + +/* See gdbsupport/pathstuff.h. */ + +gdb::unique_xmalloc_ptr<char> +gdb_abspath (const char *path) +{ + gdb_assert (path != NULL && path[0] != '\0'); + + if (path[0] == '~') + return gdb_tilde_expand_up (path); + + if (IS_ABSOLUTE_PATH (path)) + return make_unique_xstrdup (path); + + /* Beware the // my son, the Emacs barfs, the botch that catch... */ + return gdb::unique_xmalloc_ptr<char> + (concat (current_directory, + IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1]) + ? "" : SLASH_STRING, + path, (char *) NULL)); +} + +/* See gdbsupport/pathstuff.h. */ + +const char * +child_path (const char *parent, const char *child) +{ + /* The child path must start with the parent path. */ + size_t parent_len = strlen (parent); + if (filename_ncmp (parent, child, parent_len) != 0) + return NULL; + + /* The parent path must be a directory and the child must contain at + least one component underneath the parent. */ + const char *child_component; + if (parent_len > 0 && IS_DIR_SEPARATOR (parent[parent_len - 1])) + { + /* The parent path ends in a directory separator, so it is a + directory. The first child component starts after the common + prefix. */ + child_component = child + parent_len; + } + else + { + /* The parent path does not end in a directory separator. The + first character in the child after the common prefix must be + a directory separator. + + Note that CHILD must hold at least parent_len characters for + filename_ncmp to return zero. If the character at parent_len + is nul due to CHILD containing the same path as PARENT, the + IS_DIR_SEPARATOR check will fail here. */ + if (!IS_DIR_SEPARATOR (child[parent_len])) + return NULL; + + /* The first child component starts after the separator after the + common prefix. */ + child_component = child + parent_len + 1; + } + + /* The child must contain at least one non-separator character after + the parent. */ + while (*child_component != '\0') + { + if (!IS_DIR_SEPARATOR (*child_component)) + return child_component; + + child_component++; + } + return NULL; +} + +/* See gdbsupport/pathstuff.h. */ + +bool +contains_dir_separator (const char *path) +{ + for (; *path != '\0'; path++) + { + if (IS_DIR_SEPARATOR (*path)) + return true; + } + + return false; +} + +/* See gdbsupport/pathstuff.h. */ + +std::string +get_standard_cache_dir () +{ +#ifdef __APPLE__ +#define HOME_CACHE_DIR "Library/Caches" +#else +#define HOME_CACHE_DIR ".cache" +#endif + +#ifndef __APPLE__ + const char *xdg_cache_home = getenv ("XDG_CACHE_HOME"); + if (xdg_cache_home != NULL) + { + /* Make sure the path is absolute and tilde-expanded. */ + gdb::unique_xmalloc_ptr<char> abs (gdb_abspath (xdg_cache_home)); + return string_printf ("%s/gdb", abs.get ()); + } +#endif + + const char *home = getenv ("HOME"); + if (home != NULL) + { + /* Make sure the path is absolute and tilde-expanded. */ + gdb::unique_xmalloc_ptr<char> abs (gdb_abspath (home)); + return string_printf ("%s/" HOME_CACHE_DIR "/gdb", abs.get ()); + } + + return {}; +} + +/* See gdbsupport/pathstuff.h. */ + +std::string +get_standard_temp_dir () +{ +#ifdef WIN32 + const char *tmp = getenv ("TMP"); + if (tmp != nullptr) + return tmp; + + tmp = getenv ("TEMP"); + if (tmp != nullptr) + return tmp; + + error (_("Couldn't find temp dir path, both TMP and TEMP are unset.")); + +#else + const char *tmp = getenv ("TMPDIR"); + if (tmp != nullptr) + return tmp; + + return "/tmp"; +#endif +} + +/* See gdbsupport/pathstuff.h. */ + +const char * +get_shell () +{ + const char *ret = getenv ("SHELL"); + if (ret == NULL) + ret = "/bin/sh"; + + return ret; +} + +/* See gdbsupport/pathstuff.h. */ + +gdb::char_vector +make_temp_filename (const std::string &f) +{ + gdb::char_vector filename_temp (f.length () + 8); + strcpy (filename_temp.data (), f.c_str ()); + strcat (filename_temp.data () + f.size (), "-XXXXXX"); + return filename_temp; +} diff --git a/gdb/gdbsupport/pathstuff.h b/gdb/gdbsupport/pathstuff.h new file mode 100644 index 00000000000..7d7c8cd0281 --- /dev/null +++ b/gdb/gdbsupport/pathstuff.h @@ -0,0 +1,95 @@ +/* Path manipulation routines for GDB and gdbserver. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_PATHSTUFF_H +#define COMMON_PATHSTUFF_H + +#include "gdbsupport/byte-vector.h" + +/* Path utilities. */ + +/* Return the real path of FILENAME, expanding all the symbolic links. + + Contrary to "gdb_abspath", this function does not use + CURRENT_DIRECTORY for path expansion. Instead, it relies on the + current working directory (CWD) of GDB or gdbserver. */ + +extern gdb::unique_xmalloc_ptr<char> gdb_realpath (const char *filename); + +/* Return a copy of FILENAME, with its directory prefix canonicalized + by gdb_realpath. */ + +extern gdb::unique_xmalloc_ptr<char> + gdb_realpath_keepfile (const char *filename); + +/* Return PATH in absolute form, performing tilde-expansion if necessary. + PATH cannot be NULL or the empty string. + This does not resolve symlinks however, use gdb_realpath for that. + + Contrary to "gdb_realpath", this function uses CURRENT_DIRECTORY + for the path expansion. This may lead to scenarios the current + working directory (CWD) is different than CURRENT_DIRECTORY. */ + +extern gdb::unique_xmalloc_ptr<char> gdb_abspath (const char *path); + +/* If the path in CHILD is a child of the path in PARENT, return a + pointer to the first component in the CHILD's pathname below the + PARENT. Otherwise, return NULL. */ + +extern const char *child_path (const char *parent, const char *child); + +/* Return whether PATH contains a directory separator character. */ + +extern bool contains_dir_separator (const char *path); + +/* Get the usual user cache directory for the current platform. + + On Linux, it follows the XDG Base Directory specification: use + $XDG_CACHE_HOME/gdb if the XDG_CACHE_HOME environment variable is + defined, otherwise $HOME/.cache. + + On macOS, it follows the local convention and uses + ~/Library/Caches/gdb. + + The return value is absolute and tilde-expanded. Return an empty + string if neither XDG_CACHE_HOME (on Linux) or HOME are defined. */ + +extern std::string get_standard_cache_dir (); + +/* Get the usual temporary directory for the current platform. + + On Windows, this is the TMP or TEMP environment variable. + + On the rest, this is the TMPDIR environment variable, if defined, else /tmp. + + Throw an exception on error. */ + +extern std::string get_standard_temp_dir (); + +/* Return the file name of the user's shell. Normally this comes from + the SHELL environment variable. */ + +extern const char *get_shell (); + +/* Make a filename suitable to pass to mkstemp based on F (e.g. + /tmp/foo -> /tmp/foo-XXXXXX). */ + +extern gdb::char_vector make_temp_filename (const std::string &f); + +#endif /* COMMON_PATHSTUFF_H */ diff --git a/gdb/gdbsupport/poison.h b/gdb/gdbsupport/poison.h new file mode 100644 index 00000000000..699de513bab --- /dev/null +++ b/gdb/gdbsupport/poison.h @@ -0,0 +1,248 @@ +/* Poison symbols at compile time. + + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_POISON_H +#define COMMON_POISON_H + +#include "traits.h" +#include "obstack.h" + +/* Poison memset of non-POD types. The idea is catching invalid + initialization of non-POD structs that is easy to be introduced as + side effect of refactoring. For example, say this: + + struct S { VEC(foo_s) *m_data; }; + +is converted to this at some point: + + struct S { + S() { m_data.reserve (10); } + std::vector<foo> m_data; + }; + +and old code was initializing S objects like this: + + struct S s; + memset (&s, 0, sizeof (S)); // whoops, now wipes vector. + +Declaring memset as deleted for non-POD types makes the memset above +be a compile-time error. */ + +/* Helper for SFINAE. True if "T *" is memsettable. I.e., if T is + either void, or POD. */ +template<typename T> +struct IsMemsettable + : gdb::Or<std::is_void<T>, + std::is_pod<T>> +{}; + +template <typename T, + typename = gdb::Requires<gdb::Not<IsMemsettable<T>>>> +void *memset (T *s, int c, size_t n) = delete; + +#if HAVE_IS_TRIVIALLY_COPYABLE + +/* Similarly, poison memcpy and memmove of non trivially-copyable + types, which is undefined. */ + +/* True if "T *" is relocatable. I.e., copyable with memcpy/memmove. + I.e., T is either trivially copyable, or void. */ +template<typename T> +struct IsRelocatable + : gdb::Or<std::is_void<T>, + std::is_trivially_copyable<T>> +{}; + +/* True if both source and destination are relocatable. */ + +template <typename D, typename S> +using BothAreRelocatable + = gdb::And<IsRelocatable<D>, IsRelocatable<S>>; + +template <typename D, typename S, + typename = gdb::Requires<gdb::Not<BothAreRelocatable<D, S>>>> +void *memcpy (D *dest, const S *src, size_t n) = delete; + +template <typename D, typename S, + typename = gdb::Requires<gdb::Not<BothAreRelocatable<D, S>>>> +void *memmove (D *dest, const S *src, size_t n) = delete; + +#endif /* HAVE_IS_TRIVIALLY_COPYABLE */ + +/* Poison XNEW and friends to catch usages of malloc-style allocations on + objects that require new/delete. */ + +template<typename T> +#if HAVE_IS_TRIVIALLY_CONSTRUCTIBLE +using IsMallocable = std::is_trivially_constructible<T>; +#else +using IsMallocable = std::true_type; +#endif + +template<typename T> +using IsFreeable = gdb::Or<std::is_trivially_destructible<T>, std::is_void<T>>; + +template <typename T, typename = gdb::Requires<gdb::Not<IsFreeable<T>>>> +void free (T *ptr) = delete; + +template<typename T> +static T * +xnew () +{ + static_assert (IsMallocable<T>::value, "Trying to use XNEW with a non-POD \ +data type. Use operator new instead."); + return XNEW (T); +} + +#undef XNEW +#define XNEW(T) xnew<T>() + +template<typename T> +static T * +xcnew () +{ + static_assert (IsMallocable<T>::value, "Trying to use XCNEW with a non-POD \ +data type. Use operator new instead."); + return XCNEW (T); +} + +#undef XCNEW +#define XCNEW(T) xcnew<T>() + +template<typename T> +static void +xdelete (T *p) +{ + static_assert (IsFreeable<T>::value, "Trying to use XDELETE with a non-POD \ +data type. Use operator delete instead."); + XDELETE (p); +} + +#undef XDELETE +#define XDELETE(P) xdelete (P) + +template<typename T> +static T * +xnewvec (size_t n) +{ + static_assert (IsMallocable<T>::value, "Trying to use XNEWVEC with a \ +non-POD data type. Use operator new[] (or std::vector) instead."); + return XNEWVEC (T, n); +} + +#undef XNEWVEC +#define XNEWVEC(T, N) xnewvec<T> (N) + +template<typename T> +static T * +xcnewvec (size_t n) +{ + static_assert (IsMallocable<T>::value, "Trying to use XCNEWVEC with a \ +non-POD data type. Use operator new[] (or std::vector) instead."); + return XCNEWVEC (T, n); +} + +#undef XCNEWVEC +#define XCNEWVEC(T, N) xcnewvec<T> (N) + +template<typename T> +static T * +xresizevec (T *p, size_t n) +{ + static_assert (IsMallocable<T>::value, "Trying to use XRESIZEVEC with a \ +non-POD data type."); + return XRESIZEVEC (T, p, n); +} + +#undef XRESIZEVEC +#define XRESIZEVEC(T, P, N) xresizevec<T> (P, N) + +template<typename T> +static void +xdeletevec (T *p) +{ + static_assert (IsFreeable<T>::value, "Trying to use XDELETEVEC with a \ +non-POD data type. Use operator delete[] (or std::vector) instead."); + XDELETEVEC (p); +} + +#undef XDELETEVEC +#define XDELETEVEC(P) xdeletevec (P) + +template<typename T> +static T * +xnewvar (size_t s) +{ + static_assert (IsMallocable<T>::value, "Trying to use XNEWVAR with a \ +non-POD data type."); + return XNEWVAR (T, s);; +} + +#undef XNEWVAR +#define XNEWVAR(T, S) xnewvar<T> (S) + +template<typename T> +static T * +xcnewvar (size_t s) +{ + static_assert (IsMallocable<T>::value, "Trying to use XCNEWVAR with a \ +non-POD data type."); + return XCNEWVAR (T, s); +} + +#undef XCNEWVAR +#define XCNEWVAR(T, S) xcnewvar<T> (S) + +template<typename T> +static T * +xresizevar (T *p, size_t s) +{ + static_assert (IsMallocable<T>::value, "Trying to use XRESIZEVAR with a \ +non-POD data type."); + return XRESIZEVAR (T, p, s); +} + +#undef XRESIZEVAR +#define XRESIZEVAR(T, P, S) xresizevar<T> (P, S) + +template<typename T> +static T * +xobnew (obstack *ob) +{ + static_assert (IsMallocable<T>::value, "Trying to use XOBNEW with a \ +non-POD data type."); + return XOBNEW (ob, T); +} + +#undef XOBNEW +#define XOBNEW(O, T) xobnew<T> (O) + +template<typename T> +static T * +xobnewvec (obstack *ob, size_t n) +{ + static_assert (IsMallocable<T>::value, "Trying to use XOBNEWVEC with a \ +non-POD data type."); + return XOBNEWVEC (ob, T, n); +} + +#undef XOBNEWVEC +#define XOBNEWVEC(O, T, N) xobnewvec<T> (O, N) + +#endif /* COMMON_POISON_H */ diff --git a/gdb/gdbsupport/posix-strerror.c b/gdb/gdbsupport/posix-strerror.c new file mode 100644 index 00000000000..a8651b706d7 --- /dev/null +++ b/gdb/gdbsupport/posix-strerror.c @@ -0,0 +1,38 @@ +/* Safe version of strerror for POSIX systems for GDB, the GNU debugger. + + Copyright (C) 2006-2019 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" + +/* Implementation of safe_strerror as defined in common-utils.h. */ + +char * +safe_strerror (int errnum) +{ + char *msg; + + msg = strerror (errnum); + if (msg == NULL) + { + static char buf[32]; + + xsnprintf (buf, sizeof buf, "(undocumented errno %d)", errnum); + msg = buf; + } + return (msg); +} diff --git a/gdb/gdbsupport/preprocessor.h b/gdb/gdbsupport/preprocessor.h new file mode 100644 index 00000000000..cb735c34bbe --- /dev/null +++ b/gdb/gdbsupport/preprocessor.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_PREPROCESSOR_H +#define COMMON_PREPROCESSOR_H + +/* Generally useful preprocessor bits. */ + +/* Concatenate two tokens. */ +#define CONCAT_1(a, b) a ## b +#define CONCAT(a, b) CONCAT_1 (a, b) + +/* Stringification. */ +#define STRINGIFY_1(x) #x +#define STRINGIFY(x) STRINGIFY_1 (x) + +/* Escape parens out. Useful if you need to pass an argument that + includes commas to another macro. */ +#define ESC_PARENS(...) __VA_ARGS__ + +#endif /* COMMON_PREPROCESSOR_H */ diff --git a/gdb/gdbsupport/print-utils.c b/gdb/gdbsupport/print-utils.c new file mode 100644 index 00000000000..051c4541b28 --- /dev/null +++ b/gdb/gdbsupport/print-utils.c @@ -0,0 +1,326 @@ +/* Cell-based print utility routines for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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 "print-utils.h" +/* Temporary storage using circular buffer. */ + +/* Number of cells in the circular buffer. */ +#define NUMCELLS 16 + +/* Return the next entry in the circular buffer. */ + +char * +get_print_cell (void) +{ + static char buf[NUMCELLS][PRINT_CELL_SIZE]; + static int cell = 0; + + if (++cell >= NUMCELLS) + cell = 0; + return buf[cell]; +} + +static char * +decimal2str (const char *sign, ULONGEST addr, int width) +{ + /* Steal code from valprint.c:print_decimal(). Should this worry + about the real size of addr as the above does? */ + unsigned long temp[3]; + char *str = get_print_cell (); + int i = 0; + + do + { + temp[i] = addr % (1000 * 1000 * 1000); + addr /= (1000 * 1000 * 1000); + i++; + width -= 9; + } + while (addr != 0 && i < (sizeof (temp) / sizeof (temp[0]))); + + width += 9; + if (width < 0) + width = 0; + + switch (i) + { + case 1: + xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu", sign, width, temp[0]); + break; + case 2: + xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu", sign, width, + temp[1], temp[0]); + break; + case 3: + xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu%09lu", sign, width, + temp[2], temp[1], temp[0]); + break; + default: + internal_error (__FILE__, __LINE__, + _("failed internal consistency check")); + } + + return str; +} + +static char * +octal2str (ULONGEST addr, int width) +{ + unsigned long temp[3]; + char *str = get_print_cell (); + int i = 0; + + do + { + temp[i] = addr % (0100000 * 0100000); + addr /= (0100000 * 0100000); + i++; + width -= 10; + } + while (addr != 0 && i < (sizeof (temp) / sizeof (temp[0]))); + + width += 10; + if (width < 0) + width = 0; + + switch (i) + { + case 1: + if (temp[0] == 0) + xsnprintf (str, PRINT_CELL_SIZE, "%*o", width, 0); + else + xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo", width, temp[0]); + break; + case 2: + xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo", width, temp[1], temp[0]); + break; + case 3: + xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo%010lo", width, + temp[2], temp[1], temp[0]); + break; + default: + internal_error (__FILE__, __LINE__, + _("failed internal consistency check")); + } + + return str; +} + +/* See print-utils.h. */ + +char * +pulongest (ULONGEST u) +{ + return decimal2str ("", u, 0); +} + +/* See print-utils.h. */ + +char * +plongest (LONGEST l) +{ + if (l < 0) + return decimal2str ("-", -l, 0); + else + return decimal2str ("", l, 0); +} + +/* Eliminate warning from compiler on 32-bit systems. */ +static int thirty_two = 32; + +/* See print-utils.h. */ + +char * +phex (ULONGEST l, int sizeof_l) +{ + char *str; + + switch (sizeof_l) + { + case 8: + str = get_print_cell (); + xsnprintf (str, PRINT_CELL_SIZE, "%08lx%08lx", + (unsigned long) (l >> thirty_two), + (unsigned long) (l & 0xffffffff)); + break; + case 4: + str = get_print_cell (); + xsnprintf (str, PRINT_CELL_SIZE, "%08lx", (unsigned long) l); + break; + case 2: + str = get_print_cell (); + xsnprintf (str, PRINT_CELL_SIZE, "%04x", (unsigned short) (l & 0xffff)); + break; + default: + str = phex (l, sizeof (l)); + break; + } + + return str; +} + +/* See print-utils.h. */ + +char * +phex_nz (ULONGEST l, int sizeof_l) +{ + char *str; + + switch (sizeof_l) + { + case 8: + { + unsigned long high = (unsigned long) (l >> thirty_two); + + str = get_print_cell (); + if (high == 0) + xsnprintf (str, PRINT_CELL_SIZE, "%lx", + (unsigned long) (l & 0xffffffff)); + else + xsnprintf (str, PRINT_CELL_SIZE, "%lx%08lx", high, + (unsigned long) (l & 0xffffffff)); + break; + } + case 4: + str = get_print_cell (); + xsnprintf (str, PRINT_CELL_SIZE, "%lx", (unsigned long) l); + break; + case 2: + str = get_print_cell (); + xsnprintf (str, PRINT_CELL_SIZE, "%x", (unsigned short) (l & 0xffff)); + break; + default: + str = phex_nz (l, sizeof (l)); + break; + } + + return str; +} + +/* See print-utils.h. */ + +char * +hex_string (LONGEST num) +{ + char *result = get_print_cell (); + + xsnprintf (result, PRINT_CELL_SIZE, "0x%s", phex_nz (num, sizeof (num))); + return result; +} + +/* See print-utils.h. */ + +char * +hex_string_custom (LONGEST num, int width) +{ + char *result = get_print_cell (); + char *result_end = result + PRINT_CELL_SIZE - 1; + const char *hex = phex_nz (num, sizeof (num)); + int hex_len = strlen (hex); + + if (hex_len > width) + width = hex_len; + if (width + 2 >= PRINT_CELL_SIZE) + internal_error (__FILE__, __LINE__, _("\ +hex_string_custom: insufficient space to store result")); + + strcpy (result_end - width - 2, "0x"); + memset (result_end - width, '0', width); + strcpy (result_end - hex_len, hex); + return result_end - width - 2; +} + +/* See print-utils.h. */ + +char * +int_string (LONGEST val, int radix, int is_signed, int width, + int use_c_format) +{ + switch (radix) + { + case 16: + { + char *result; + + if (width == 0) + result = hex_string (val); + else + result = hex_string_custom (val, width); + if (! use_c_format) + result += 2; + return result; + } + case 10: + { + if (is_signed && val < 0) + return decimal2str ("-", -val, width); + else + return decimal2str ("", val, width); + } + case 8: + { + char *result = octal2str (val, width); + + if (use_c_format || val == 0) + return result; + else + return result + 1; + } + default: + internal_error (__FILE__, __LINE__, + _("failed internal consistency check")); + } +} + +/* See print-utils.h. */ + +const char * +core_addr_to_string (const CORE_ADDR addr) +{ + char *str = get_print_cell (); + + strcpy (str, "0x"); + strcat (str, phex (addr, sizeof (addr))); + return str; +} + +/* See print-utils.h. */ + +const char * +core_addr_to_string_nz (const CORE_ADDR addr) +{ + char *str = get_print_cell (); + + strcpy (str, "0x"); + strcat (str, phex_nz (addr, sizeof (addr))); + return str; +} + +/* See print-utils.h. */ + +const char * +host_address_to_string_1 (const void *addr) +{ + char *str = get_print_cell (); + + xsnprintf (str, PRINT_CELL_SIZE, "0x%s", + phex_nz ((uintptr_t) addr, sizeof (addr))); + return str; +} diff --git a/gdb/gdbsupport/print-utils.h b/gdb/gdbsupport/print-utils.h new file mode 100644 index 00000000000..815b14cbed3 --- /dev/null +++ b/gdb/gdbsupport/print-utils.h @@ -0,0 +1,82 @@ +/* Cell-based print utility routines for GDB, the GNU debugger. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_PRINT_UTILS_H +#define COMMON_PRINT_UTILS_H + +/* How many characters (including the terminating null byte) fit in a + cell. */ +#define PRINT_CELL_SIZE 50 + +/* %d for LONGEST. The result is stored in a circular static buffer, + NUMCELLS deep. */ + +extern char *pulongest (ULONGEST u); + +/* %u for ULONGEST. The result is stored in a circular static buffer, + NUMCELLS deep. */ + +extern char *plongest (LONGEST l); + +extern char *phex (ULONGEST l, int sizeof_l); + +/* Convert a ULONGEST into a HEX string, like %lx. The result is + stored in a circular static buffer, NUMCELLS deep. */ + +extern char *phex_nz (ULONGEST l, int sizeof_l); + +/* Converts a LONGEST to a C-format hexadecimal literal and stores it + in a static string. Returns a pointer to this string. */ + +extern char *hex_string (LONGEST num); + +/* Converts a LONGEST number to a C-format hexadecimal literal and + stores it in a static string. Returns a pointer to this string + that is valid until the next call. The number is padded on the + left with 0s to at least WIDTH characters. */ + +extern char *hex_string_custom (LONGEST num, int width); + +/* Convert VAL to a numeral in the given radix. For + * radix 10, IS_SIGNED may be true, indicating a signed quantity; + * otherwise VAL is interpreted as unsigned. If WIDTH is supplied, + * it is the minimum width (0-padded if needed). USE_C_FORMAT means + * to use C format in all cases. If it is false, then 'x' + * and 'o' formats do not include a prefix (0x or leading 0). */ + +extern char *int_string (LONGEST val, int radix, int is_signed, int width, + int use_c_format); + +/* Convert a CORE_ADDR into a string. */ + +extern const char *core_addr_to_string (const CORE_ADDR addr); + +extern const char *core_addr_to_string_nz (const CORE_ADDR addr); + +extern const char *host_address_to_string_1 (const void *addr); + +/* Wrapper that avoids adding a pointless cast to all callers. */ +#define host_address_to_string(ADDR) \ + host_address_to_string_1 ((const void *) (ADDR)) + +/* Return the next entry in the circular print buffer. */ + +extern char *get_print_cell (void); + +#endif /* COMMON_PRINT_UTILS_H */ diff --git a/gdb/gdbsupport/ptid.c b/gdb/gdbsupport/ptid.c new file mode 100644 index 00000000000..c025511a37b --- /dev/null +++ b/gdb/gdbsupport/ptid.c @@ -0,0 +1,26 @@ +/* The ptid_t type and common functions operating on it. + + Copyright (C) 1986-2019 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 "ptid.h" + +/* See ptid.h for these. */ + +ptid_t const null_ptid = ptid_t::make_null (); +ptid_t const minus_one_ptid = ptid_t::make_minus_one (); diff --git a/gdb/gdbsupport/ptid.h b/gdb/gdbsupport/ptid.h new file mode 100644 index 00000000000..f5625a61387 --- /dev/null +++ b/gdb/gdbsupport/ptid.h @@ -0,0 +1,155 @@ +/* The ptid_t type and common functions operating on it. + + Copyright (C) 1986-2019 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/>. */ + +#ifndef COMMON_PTID_H +#define COMMON_PTID_H + +/* The ptid struct is a collection of the various "ids" necessary for + identifying the inferior process/thread being debugged. This + consists of the process id (pid), lightweight process id (lwp) and + thread id (tid). When manipulating ptids, the constructors, + accessors, and predicates declared in this file should be used. Do + NOT access the struct ptid members directly. + + process_stratum targets that handle threading themselves should + prefer using the ptid.lwp field, leaving the ptid.tid field for any + thread_stratum target that might want to sit on top. +*/ + +class ptid_t +{ +public: + /* Must have a trivial defaulted default constructor so that the + type remains POD. */ + ptid_t () noexcept = default; + + /* Make a ptid given the necessary PID, LWP, and TID components. + + A ptid with only a PID (LWP and TID equal to zero) is usually used to + represent a whole process, including all its lwps/threads. */ + + explicit constexpr ptid_t (int pid, long lwp = 0, long tid = 0) + : m_pid (pid), m_lwp (lwp), m_tid (tid) + {} + + /* Fetch the pid (process id) component from the ptid. */ + + constexpr int pid () const + { return m_pid; } + + /* Return true if the ptid's lwp member is non-zero. */ + + constexpr bool lwp_p () const + { return m_lwp != 0; } + + /* Fetch the lwp (lightweight process) component from the ptid. */ + + constexpr long lwp () const + { return m_lwp; } + + /* Return true if the ptid's tid member is non-zero. */ + + constexpr bool tid_p () const + { return m_tid != 0; } + + /* Fetch the tid (thread id) component from a ptid. */ + + constexpr long tid () const + { return m_tid; } + + /* Return true if the ptid represents a whole process, including all its + lwps/threads. Such ptids have the form of (pid, 0, 0), with + pid != -1. */ + + constexpr bool is_pid () const + { + return (*this != make_null () + && *this != make_minus_one () + && m_lwp == 0 + && m_tid == 0); + } + + /* Compare two ptids to see if they are equal. */ + + constexpr bool operator== (const ptid_t &other) const + { + return (m_pid == other.m_pid + && m_lwp == other.m_lwp + && m_tid == other.m_tid); + } + + /* Compare two ptids to see if they are different. */ + + constexpr bool operator!= (const ptid_t &other) const + { + return !(*this == other); + } + + /* Return true if the ptid matches FILTER. FILTER can be the wild + card MINUS_ONE_PTID (all ptids match it); can be a ptid representing + a process (ptid.is_pid () returns true), in which case, all lwps and + threads of that given process match, lwps and threads of other + processes do not; or, it can represent a specific thread, in which + case, only that thread will match true. The ptid must represent a + specific LWP or THREAD, it can never be a wild card. */ + + constexpr bool matches (const ptid_t &filter) const + { + return (/* If filter represents any ptid, it's always a match. */ + filter == make_minus_one () + /* If filter is only a pid, any ptid with that pid + matches. */ + || (filter.is_pid () && m_pid == filter.pid ()) + + /* Otherwise, this ptid only matches if it's exactly equal + to filter. */ + || *this == filter); + } + + /* Make a null ptid. */ + + static constexpr ptid_t make_null () + { return ptid_t (0, 0, 0); } + + /* Make a minus one ptid. */ + + static constexpr ptid_t make_minus_one () + { return ptid_t (-1, 0, 0); } + +private: + /* Process id. */ + int m_pid; + + /* Lightweight process id. */ + long m_lwp; + + /* Thread id. */ + long m_tid; +}; + +/* The null or zero ptid, often used to indicate no process. */ + +extern const ptid_t null_ptid; + +/* The (-1,0,0) ptid, often used to indicate either an error condition + or a "don't care" condition, i.e, "run all threads." */ + +extern const ptid_t minus_one_ptid; + +#endif /* COMMON_PTID_H */ diff --git a/gdb/gdbsupport/refcounted-object.h b/gdb/gdbsupport/refcounted-object.h new file mode 100644 index 00000000000..4bf7fcf1c94 --- /dev/null +++ b/gdb/gdbsupport/refcounted-object.h @@ -0,0 +1,70 @@ +/* Base class of intrusively reference-counted objects. + Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_REFCOUNTED_OBJECT_H +#define COMMON_REFCOUNTED_OBJECT_H + +/* Base class of intrusively reference-countable objects. + Incrementing and decrementing the reference count is an external + responsibility. */ + +class refcounted_object +{ +public: + refcounted_object () = default; + + /* Increase the refcount. */ + void incref () + { + gdb_assert (m_refcount >= 0); + m_refcount++; + } + + /* Decrease the refcount. */ + void decref () + { + m_refcount--; + gdb_assert (m_refcount >= 0); + } + + int refcount () const { return m_refcount; } + +private: + DISABLE_COPY_AND_ASSIGN (refcounted_object); + + /* The reference count. */ + int m_refcount = 0; +}; + +/* A policy class to interface gdb::ref_ptr with a + refcounted_object. */ + +struct refcounted_object_ref_policy +{ + static void incref (refcounted_object *ptr) + { + ptr->incref (); + } + + static void decref (refcounted_object *ptr) + { + ptr->decref (); + } +}; + +#endif /* COMMON_REFCOUNTED_OBJECT_H */ diff --git a/gdb/gdbsupport/rsp-low.c b/gdb/gdbsupport/rsp-low.c new file mode 100644 index 00000000000..4bb49418499 --- /dev/null +++ b/gdb/gdbsupport/rsp-low.c @@ -0,0 +1,307 @@ +/* Low-level RSP routines for GDB, the GNU debugger. + + Copyright (C) 1988-2019 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 "rsp-low.h" + +/* See rsp-low.h. */ + +int +fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else + error (_("Reply contains invalid hex digit %d"), a); +} + +/* See rsp-low.h. */ + +int +tohex (int nib) +{ + if (nib < 10) + return '0' + nib; + else + return 'a' + nib - 10; +} + +/* Encode 64 bits in 16 chars of hex. */ + +static const char hexchars[] = "0123456789abcdef"; + +static int +ishex (int ch, int *val) +{ + if ((ch >= 'a') && (ch <= 'f')) + { + *val = ch - 'a' + 10; + return 1; + } + if ((ch >= 'A') && (ch <= 'F')) + { + *val = ch - 'A' + 10; + return 1; + } + if ((ch >= '0') && (ch <= '9')) + { + *val = ch - '0'; + return 1; + } + return 0; +} + +/* See rsp-low.h. */ + +char * +pack_nibble (char *buf, int nibble) +{ + *buf++ = hexchars[(nibble & 0x0f)]; + return buf; +} + +/* See rsp-low.h. */ + +char * +pack_hex_byte (char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +/* See rsp-low.h. */ + +const char * +unpack_varlen_hex (const char *buff, /* packet to parse */ + ULONGEST *result) +{ + int nibble; + ULONGEST retval = 0; + + while (ishex (*buff, &nibble)) + { + buff++; + retval = retval << 4; + retval |= nibble & 0x0f; + } + *result = retval; + return buff; +} + +/* See rsp-low.h. */ + +int +hex2bin (const char *hex, gdb_byte *bin, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + if (hex[0] == 0 || hex[1] == 0) + { + /* Hex string is short, or of uneven length. + Return the count that has been converted so far. */ + return i; + } + *bin++ = fromhex (hex[0]) * 16 + fromhex (hex[1]); + hex += 2; + } + return i; +} + +/* See rsp-low.h. */ + +gdb::byte_vector +hex2bin (const char *hex) +{ + size_t bin_len = strlen (hex) / 2; + gdb::byte_vector bin (bin_len); + + hex2bin (hex, bin.data (), bin_len); + + return bin; +} + +/* See rsp-low.h. */ + +std::string +hex2str (const char *hex) +{ + return hex2str (hex, strlen (hex)); +} + +/* See rsp-low.h. */ + +std::string +hex2str (const char *hex, int count) +{ + std::string ret; + + ret.reserve (count); + for (size_t i = 0; i < count; ++i) + { + if (hex[0] == '\0' || hex[1] == '\0') + { + /* Hex string is short, or of uneven length. Return what we + have so far. */ + return ret; + } + ret += fromhex (hex[0]) * 16 + fromhex (hex[1]); + hex += 2; + } + + return ret; +} + +/* See rsp-low.h. */ + +int +bin2hex (const gdb_byte *bin, char *hex, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + *hex++ = tohex ((*bin >> 4) & 0xf); + *hex++ = tohex (*bin++ & 0xf); + } + *hex = 0; + return i; +} + +/* See rsp-low.h. */ + +std::string +bin2hex (const gdb_byte *bin, int count) +{ + std::string ret; + + ret.reserve (count * 2); + for (int i = 0; i < count; ++i) + { + ret += tohex ((*bin >> 4) & 0xf); + ret += tohex (*bin++ & 0xf); + } + + return ret; +} + +/* Return whether byte B needs escaping when sent as part of binary data. */ + +static int +needs_escaping (gdb_byte b) +{ + return b == '$' || b == '#' || b == '}' || b == '*'; +} + +/* See rsp-low.h. */ + +int +remote_escape_output (const gdb_byte *buffer, int len_units, int unit_size, + gdb_byte *out_buf, int *out_len_units, + int out_maxlen_bytes) +{ + int input_unit_index, output_byte_index = 0, byte_index_in_unit; + int number_escape_bytes_needed; + + /* Try to copy integral addressable memory units until + (1) we run out of space or + (2) we copied all of them. */ + for (input_unit_index = 0; + input_unit_index < len_units; + input_unit_index++) + { + /* Find out how many escape bytes we need for this unit. */ + number_escape_bytes_needed = 0; + for (byte_index_in_unit = 0; + byte_index_in_unit < unit_size; + byte_index_in_unit++) + { + int idx = input_unit_index * unit_size + byte_index_in_unit; + gdb_byte b = buffer[idx]; + if (needs_escaping (b)) + number_escape_bytes_needed++; + } + + /* Check if we have room to fit this escaped unit. */ + if (output_byte_index + unit_size + number_escape_bytes_needed > + out_maxlen_bytes) + break; + + /* Copy the unit byte per byte, adding escapes. */ + for (byte_index_in_unit = 0; + byte_index_in_unit < unit_size; + byte_index_in_unit++) + { + int idx = input_unit_index * unit_size + byte_index_in_unit; + gdb_byte b = buffer[idx]; + if (needs_escaping (b)) + { + out_buf[output_byte_index++] = '}'; + out_buf[output_byte_index++] = b ^ 0x20; + } + else + out_buf[output_byte_index++] = b; + } + } + + *out_len_units = input_unit_index; + return output_byte_index; +} + +/* See rsp-low.h. */ + +int +remote_unescape_input (const gdb_byte *buffer, int len, + gdb_byte *out_buf, int out_maxlen) +{ + int input_index, output_index; + int escaped; + + output_index = 0; + escaped = 0; + for (input_index = 0; input_index < len; input_index++) + { + gdb_byte b = buffer[input_index]; + + if (output_index + 1 > out_maxlen) + error (_("Received too much data from the target.")); + + if (escaped) + { + out_buf[output_index++] = b ^ 0x20; + escaped = 0; + } + else if (b == '}') + escaped = 1; + else + out_buf[output_index++] = b; + } + + if (escaped) + error (_("Unmatched escape character in target response.")); + + return output_index; +} + diff --git a/gdb/gdbsupport/rsp-low.h b/gdb/gdbsupport/rsp-low.h new file mode 100644 index 00000000000..b2b62c9e5b8 --- /dev/null +++ b/gdb/gdbsupport/rsp-low.h @@ -0,0 +1,101 @@ +/* Low-level RSP routines for GDB, the GNU debugger. + + Copyright (C) 1988-2019 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/>. */ + +#ifndef COMMON_RSP_LOW_H +#define COMMON_RSP_LOW_H + +#include "gdbsupport/byte-vector.h" + +/* Convert hex digit A to a number, or throw an exception. */ + +extern int fromhex (int a); + +/* Convert number NIB to a hex digit. */ + +extern int tohex (int nib); + +/* Write a character representing the low order four bits of NIBBLE in + hex to *BUF. Returns BUF+1. */ + +extern char *pack_nibble (char *buf, int nibble); + +/* Write the low byte of BYTE in hex to *BUF. Returns BUF+2. */ + +extern char *pack_hex_byte (char *pkt, int byte); + +/* Read hex digits from BUFF and convert to a number, which is stored + in RESULT. Reads until a non-hex digit is seen. Returns a pointer + to the terminating character. */ + +extern const char *unpack_varlen_hex (const char *buff, ULONGEST *result); + +/* HEX is a string of characters representing hexadecimal digits. + Convert pairs of hex digits to bytes and store sequentially into + BIN. COUNT is the maximum number of characters to convert. This + will convert fewer characters if the number of hex characters + actually seen is odd, or if HEX terminates before COUNT characters. + Returns the number of characters actually converted. */ + +extern int hex2bin (const char *hex, gdb_byte *bin, int count); + +/* Like the above, but return a gdb::byte_vector. */ + +gdb::byte_vector hex2bin (const char *hex); + +/* Like hex2bin, but return a std::string. */ + +extern std::string hex2str (const char *hex); + +/* Like hex2bin, but return a std::string. */ + +extern std::string hex2str (const char *hex, int count); + +/* Convert some bytes to a hexadecimal representation. BIN holds the + bytes to convert. COUNT says how many bytes to convert. The + resulting characters are stored in HEX, followed by a NUL + character. Returns the number of bytes actually converted. */ + +extern int bin2hex (const gdb_byte *bin, char *hex, int count); + +/* Overloaded version of bin2hex that returns a std::string. */ + +extern std::string bin2hex (const gdb_byte *bin, int count); + +/* Convert BUFFER, binary data at least LEN_UNITS addressable memory units + long, into escaped binary data in OUT_BUF. Only copy memory units that fit + completely in OUT_BUF. Set *OUT_LEN_UNITS to the number of units from + BUFFER successfully encoded in OUT_BUF, and return the number of bytes used + in OUT_BUF. The total number of bytes in the output buffer will be at most + OUT_MAXLEN_BYTES. This function properly escapes '*', and so is suitable + for the server side as well as the client. */ + +extern int remote_escape_output (const gdb_byte *buffer, int len_units, + int unit_size, gdb_byte *out_buf, + int *out_len_units, int out_maxlen_bytes); + +/* Convert BUFFER, escaped data LEN bytes long, into binary data + in OUT_BUF. Return the number of bytes written to OUT_BUF. + Raise an error if the total number of bytes exceeds OUT_MAXLEN. + + This function reverses remote_escape_output. */ + +extern int remote_unescape_input (const gdb_byte *buffer, int len, + gdb_byte *out_buf, int out_maxlen); + +#endif /* COMMON_RSP_LOW_H */ diff --git a/gdb/gdbsupport/run-time-clock.c b/gdb/gdbsupport/run-time-clock.c new file mode 100644 index 00000000000..26378a05d91 --- /dev/null +++ b/gdb/gdbsupport/run-time-clock.c @@ -0,0 +1,58 @@ +/* User/system CPU time clocks that follow the std::chrono interface. + Copyright (C) 2016-2019 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 "run-time-clock.h" +#if defined HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +using namespace std::chrono; + +run_time_clock::time_point +run_time_clock::now () noexcept +{ + return time_point (microseconds (get_run_time ())); +} + +#ifdef HAVE_GETRUSAGE +static std::chrono::microseconds +timeval_to_microseconds (struct timeval *tv) +{ + return (seconds (tv->tv_sec) + microseconds (tv->tv_usec)); +} +#endif + +void +run_time_clock::now (user_cpu_time_clock::time_point &user, + system_cpu_time_clock::time_point &system) noexcept +{ +#ifdef HAVE_GETRUSAGE + struct rusage rusage; + + getrusage (RUSAGE_SELF, &rusage); + + microseconds utime = timeval_to_microseconds (&rusage.ru_utime); + microseconds stime = timeval_to_microseconds (&rusage.ru_stime); + user = user_cpu_time_clock::time_point (utime); + system = system_cpu_time_clock::time_point (stime); +#else + user = user_cpu_time_clock::time_point (microseconds (get_run_time ())); + system = system_cpu_time_clock::time_point (microseconds::zero ()); +#endif +} diff --git a/gdb/gdbsupport/run-time-clock.h b/gdb/gdbsupport/run-time-clock.h new file mode 100644 index 00000000000..9c15dd7ab12 --- /dev/null +++ b/gdb/gdbsupport/run-time-clock.h @@ -0,0 +1,75 @@ +/* User/system CPU time clocks that follow the std::chrono interface. + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_RUN_TIME_CLOCK_H +#define COMMON_RUN_TIME_CLOCK_H + +#include <chrono> + +/* Count the total amount of time spent executing in user mode. */ + +struct user_cpu_time_clock +{ + using duration = std::chrono::microseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<user_cpu_time_clock>; + + static constexpr bool is_steady = true; + + /* Use run_time_clock::now instead. */ + static time_point now () noexcept = delete; +}; + +/* Count the total amount of time spent executing in kernel mode. */ + +struct system_cpu_time_clock +{ + using duration = std::chrono::microseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<system_cpu_time_clock>; + + static constexpr bool is_steady = true; + + /* Use run_time_clock::now instead. */ + static time_point now () noexcept = delete; +}; + +/* Count the total amount of time spent executing in userspace+kernel + mode. */ + +struct run_time_clock +{ + using duration = std::chrono::microseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<run_time_clock>; + + static constexpr bool is_steady = true; + + static time_point now () noexcept; + + /* Return the user/system time as separate time points, if + supported. If not supported, then the combined user+kernel time + is returned in USER and SYSTEM is set to zero. */ + static void now (user_cpu_time_clock::time_point &user, + system_cpu_time_clock::time_point &system) noexcept; +}; + +#endif /* COMMON_RUN_TIME_CLOCK_H */ diff --git a/gdb/gdbsupport/safe-iterator.h b/gdb/gdbsupport/safe-iterator.h new file mode 100644 index 00000000000..89aec01884f --- /dev/null +++ b/gdb/gdbsupport/safe-iterator.h @@ -0,0 +1,93 @@ +/* A safe iterator for GDB, the GNU debugger. + Copyright (C) 2018-2019 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/>. */ + +#ifndef COMMON_SAFE_ITERATOR_H +#define COMMON_SAFE_ITERATOR_H + +/* A forward iterator that wraps Iterator, such that when iterating + with iterator IT, it is possible to delete *IT without invalidating + IT. Suitably wrapped in a range type and used with range-for, this + allow convenient patterns like this: + + // range_safe() returns a range type whose begin()/end() methods + // return safe iterators. + for (foo *f : range_safe ()) + { + if (f->should_delete ()) + { + // The ++it operation implicitly done by the range-for is + // still OK after this. + delete f; + } + } +*/ + +template<typename Iterator> +class basic_safe_iterator +{ +public: + typedef basic_safe_iterator self_type; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::reference reference; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::iterator_category iterator_category; + typedef typename Iterator::difference_type difference_type; + + /* Construct by forwarding all arguments to the underlying + iterator. */ + template<typename... Args> + explicit basic_safe_iterator (Args &&...args) + : m_it (std::forward<Args> (args)...), + m_next (m_it) + { + if (m_it != m_end) + ++m_next; + } + + /* Create a one-past-end iterator. */ + basic_safe_iterator () + {} + + value_type operator* () const { return *m_it; } + + self_type &operator++ () + { + m_it = m_next; + if (m_it != m_end) + ++m_next; + return *this; + } + + bool operator== (const self_type &other) const + { return m_it == other.m_it; } + + bool operator!= (const self_type &other) const + { return m_it != other.m_it; } + +private: + /* The current element. */ + Iterator m_it {}; + + /* The next element. Always one element ahead of M_IT. */ + Iterator m_next {}; + + /* A one-past-end iterator. */ + Iterator m_end {}; +}; + +#endif /* COMMON_SAFE_ITERATOR_H */ diff --git a/gdb/gdbsupport/scope-exit.h b/gdb/gdbsupport/scope-exit.h new file mode 100644 index 00000000000..e2e6818295e --- /dev/null +++ b/gdb/gdbsupport/scope-exit.h @@ -0,0 +1,186 @@ +/* Copyright (C) 2019 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/>. */ + +#ifndef COMMON_SCOPE_EXIT_H +#define COMMON_SCOPE_EXIT_H + +#include <functional> +#include <type_traits> +#include "gdbsupport/preprocessor.h" + +/* scope_exit is a general-purpose scope guard that calls its exit + function at the end of the current scope. A scope_exit may be + canceled by calling the "release" method. The API is modeled on + P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard + Library, which is itself based on Andrej Alexandrescu's + ScopeGuard/SCOPE_EXIT. + + There are two forms available: + + - The "make_scope_exit" form allows canceling the scope guard. Use + it like this: + + auto cleanup = make_scope_exit ( <function, function object, lambda> ); + ... + cleanup.release (); // cancel + + - If you don't need to cancel the guard, you can use the SCOPE_EXIT + macro, like this: + + SCOPE_EXIT + { + // any code you like here. + } + + See also forward_scope_exit. +*/ + +/* CRTP base class for cancelable scope_exit-like classes. Implements + the common call-custom-function-from-dtor functionality. Classes + that inherit this implement the on_exit() method, which is called + from scope_exit_base's dtor. */ + +template <typename CRTP> +class scope_exit_base +{ +public: + scope_exit_base () = default; + + ~scope_exit_base () + { + if (!m_released) + { + auto *self = static_cast<CRTP *> (this); + self->on_exit (); + } + } + + /* This is needed for make_scope_exit because copy elision isn't + guaranteed until C++17. An optimizing compiler will usually skip + calling this, but it must exist. */ + scope_exit_base (const scope_exit_base &other) + : m_released (other.m_released) + { + other.m_released = true; + } + + void operator= (const scope_exit_base &) = delete; + + /* If this is called, then the wrapped function will not be called + on destruction. */ + void release () noexcept + { + m_released = true; + } + +private: + + /* True if released. Mutable because of the copy ctor hack + above. */ + mutable bool m_released = false; +}; + +/* The scope_exit class. */ + +template<typename EF> +class scope_exit : public scope_exit_base<scope_exit<EF>> +{ + /* For access to on_exit(). */ + friend scope_exit_base<scope_exit<EF>>; + +public: + + template<typename EFP, + typename = gdb::Requires<std::is_constructible<EF, EFP>>> + scope_exit (EFP &&f) + try : m_exit_function ((!std::is_lvalue_reference<EFP>::value + && std::is_nothrow_constructible<EF, EFP>::value) + ? std::move (f) + : f) + { + } + catch (...) + { + /* "If the initialization of exit_function throws an exception, + calls f()." */ + f (); + } + + template<typename EFP, + typename = gdb::Requires<std::is_constructible<EF, EFP>>> + scope_exit (scope_exit &&rhs) + noexcept (std::is_nothrow_move_constructible<EF>::value + || std::is_nothrow_copy_constructible<EF>::value) + : m_exit_function (std::is_nothrow_constructible<EFP>::value + ? std::move (rhs) + : rhs) + { + rhs.release (); + } + + /* This is needed for make_scope_exit because copy elision isn't + guaranteed until C++17. An optimizing compiler will usually skip + calling this, but it must exist. */ + scope_exit (const scope_exit &other) + : scope_exit_base<scope_exit<EF>> (other), + m_exit_function (other.m_exit_function) + { + } + + void operator= (const scope_exit &) = delete; + void operator= (scope_exit &&) = delete; + +private: + void on_exit () + { + m_exit_function (); + } + + /* The function to call on scope exit. */ + EF m_exit_function; +}; + +template <typename EF> +scope_exit<typename std::decay<EF>::type> +make_scope_exit (EF &&f) +{ + return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); +} + +namespace detail +{ + +enum class scope_exit_lhs {}; + +template<typename EF> +scope_exit<typename std::decay<EF>::type> +operator+ (scope_exit_lhs, EF &&rhs) +{ + return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); +} + +} + +/* Register a block of code to run on scope exit. Note that the local + context is captured by reference, which means you should be careful + to avoid inadvertently changing a captured local's value before the + scope exit runs. */ + +#define SCOPE_EXIT \ + auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () + +#endif /* COMMON_SCOPE_EXIT_H */ diff --git a/gdb/gdbsupport/scoped_fd.h b/gdb/gdbsupport/scoped_fd.h new file mode 100644 index 00000000000..c4a494b7018 --- /dev/null +++ b/gdb/gdbsupport/scoped_fd.h @@ -0,0 +1,87 @@ +/* scoped_fd, automatically close a file descriptor + + Copyright (C) 2018-2019 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/>. */ + +#ifndef COMMON_SCOPED_FD_H +#define COMMON_SCOPED_FD_H + +#include <unistd.h> +#include "filestuff.h" + +/* A smart-pointer-like class to automatically close a file descriptor. */ + +class scoped_fd +{ +public: + explicit scoped_fd (int fd = -1) noexcept : m_fd (fd) {} + + scoped_fd (scoped_fd &&other) + : m_fd (other.m_fd) + { + other.m_fd = -1; + } + + ~scoped_fd () + { + if (m_fd >= 0) + close (m_fd); + } + + scoped_fd &operator= (scoped_fd &&other) + { + if (m_fd != other.m_fd) + { + if (m_fd >= 0) + close (m_fd); + m_fd = other.m_fd; + other.m_fd = -1; + } + return *this; + } + + DISABLE_COPY_AND_ASSIGN (scoped_fd); + + ATTRIBUTE_UNUSED_RESULT int release () noexcept + { + int fd = m_fd; + m_fd = -1; + return fd; + } + + /* Like release, but return a gdb_file_up that owns the file + descriptor. On success, this scoped_fd will be released. On + failure, return NULL and leave this scoped_fd in possession of + the fd. */ + gdb_file_up to_file (const char *mode) noexcept + { + gdb_file_up result (fdopen (m_fd, mode)); + if (result != nullptr) + m_fd = -1; + return result; + } + + int get () const noexcept + { + return m_fd; + } + +private: + int m_fd; +}; + +#endif /* COMMON_SCOPED_FD_H */ diff --git a/gdb/gdbsupport/scoped_mmap.c b/gdb/gdbsupport/scoped_mmap.c new file mode 100644 index 00000000000..9e0af57e4c9 --- /dev/null +++ b/gdb/gdbsupport/scoped_mmap.c @@ -0,0 +1,49 @@ +/* scoped_mmap, automatically unmap files + + Copyright (C) 2018-2019 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 "scoped_mmap.h" +#include "scoped_fd.h" +#include "gdbsupport/filestuff.h" + +#ifdef HAVE_SYS_MMAN_H + +scoped_mmap +mmap_file (const char *filename) +{ + scoped_fd fd (gdb_open_cloexec (filename, O_RDONLY, 0)); + if (fd.get () < 0) + perror_with_name (("open")); + + off_t size = lseek (fd.get (), 0, SEEK_END); + if (size < 0) + perror_with_name (("lseek")); + + /* We can't map an empty file. */ + if (size == 0) + error (_("file to mmap is empty")); + + scoped_mmap mmapped_file (nullptr, size, PROT_READ, MAP_PRIVATE, fd.get (), 0); + if (mmapped_file.get () == MAP_FAILED) + perror_with_name (("mmap")); + + return mmapped_file; +} + +#endif /* HAVE_SYS_MMAN_H */ diff --git a/gdb/gdbsupport/scoped_mmap.h b/gdb/gdbsupport/scoped_mmap.h new file mode 100644 index 00000000000..05cca9efa7d --- /dev/null +++ b/gdb/gdbsupport/scoped_mmap.h @@ -0,0 +1,94 @@ +/* scoped_mmap, automatically unmap files + + Copyright (C) 2018-2019 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/>. */ + +#ifndef COMMON_SCOPED_MMAP_H +#define COMMON_SCOPED_MMAP_H + +#ifdef HAVE_SYS_MMAN_H + +#include <sys/mman.h> + +/* A smart-pointer-like class to mmap() and automatically munmap() a memory + mapping. */ + +class scoped_mmap +{ +public: + scoped_mmap () noexcept : m_mem (MAP_FAILED), m_length (0) {} + scoped_mmap (void *addr, size_t length, int prot, int flags, int fd, + off_t offset) noexcept : m_length (length) + { + m_mem = mmap (addr, m_length, prot, flags, fd, offset); + } + + ~scoped_mmap () + { + destroy (); + } + + scoped_mmap (scoped_mmap &&rhs) + { + destroy (); + + m_mem = rhs.m_mem; + m_length = rhs.m_length; + + rhs.m_mem = MAP_FAILED; + rhs.m_length = 0; + } + + DISABLE_COPY_AND_ASSIGN (scoped_mmap); + + ATTRIBUTE_UNUSED_RESULT void *release () noexcept + { + void *mem = m_mem; + m_mem = MAP_FAILED; + m_length = 0; + return mem; + } + + void reset (void *addr, size_t length, int prot, int flags, int fd, + off_t offset) noexcept + { + destroy (); + + m_length = length; + m_mem = mmap (addr, m_length, prot, flags, fd, offset); + } + + size_t size () const noexcept { return m_length; } + void *get () const noexcept { return m_mem; } + +private: + void destroy () + { + if (m_mem != MAP_FAILED) + munmap (m_mem, m_length); + } + + void *m_mem; + size_t m_length; +}; + +/* Map FILENAME in memory. Throw an error if anything goes wrong. */ +scoped_mmap mmap_file (const char *filename); + +#endif /* HAVE_SYS_MMAN_H */ + +#endif /* COMMON_SCOPED_MMAP_H */ diff --git a/gdb/gdbsupport/scoped_restore.h b/gdb/gdbsupport/scoped_restore.h new file mode 100644 index 00000000000..54c3ff5f8a5 --- /dev/null +++ b/gdb/gdbsupport/scoped_restore.h @@ -0,0 +1,118 @@ +/* scoped_restore, a simple class for saving and restoring a value + + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_SCOPED_RESTORE_H +#define COMMON_SCOPED_RESTORE_H + +/* Base class for scoped_restore_tmpl. */ +class scoped_restore_base +{ +public: + /* This informs the (scoped_restore_tmpl<T>) dtor that you no longer + want the original value restored. */ + void release () const + { m_saved_var = NULL; } + +protected: + scoped_restore_base (void *saved_var) + : m_saved_var (saved_var) + {} + + /* The type-erased saved variable. This is here so that clients can + call release() on a "scoped_restore" local, which is a typedef to + a scoped_restore_base. See below. */ + mutable void *m_saved_var; +}; + +/* A convenience typedef. Users of make_scoped_restore declare the + local RAII object as having this type. */ +typedef const scoped_restore_base &scoped_restore; + +/* An RAII-based object that saves a variable's value, and then + restores it again when this object is destroyed. */ +template<typename T> +class scoped_restore_tmpl : public scoped_restore_base +{ + public: + + /* Create a new scoped_restore object that saves the current value + of *VAR. *VAR will be restored when this scoped_restore object + is destroyed. */ + scoped_restore_tmpl (T *var) + : scoped_restore_base (var), + m_saved_value (*var) + { + } + + /* Create a new scoped_restore object that saves the current value + of *VAR, and sets *VAR to VALUE. *VAR will be restored when this + scoped_restore object is destroyed. This is templated on T2 to + allow passing VALUEs of types convertible to T. + E.g.: T='base'; T2='derived'. */ + template <typename T2> + scoped_restore_tmpl (T *var, T2 value) + : scoped_restore_base (var), + m_saved_value (*var) + { + *var = value; + } + + scoped_restore_tmpl (const scoped_restore_tmpl<T> &other) + : scoped_restore_base {other.m_saved_var}, + m_saved_value (other.m_saved_value) + { + other.m_saved_var = NULL; + } + + ~scoped_restore_tmpl () + { + if (saved_var () != NULL) + *saved_var () = m_saved_value; + } + +private: + /* Return a pointer to the saved variable with its type + restored. */ + T *saved_var () + { return static_cast<T *> (m_saved_var); } + + /* No need for this. It is intentionally not defined anywhere. */ + scoped_restore_tmpl &operator= (const scoped_restore_tmpl &); + + /* The saved value. */ + const T m_saved_value; +}; + +/* Make a scoped_restore. This is useful because it lets template + argument deduction work. */ +template<typename T> +scoped_restore_tmpl<T> make_scoped_restore (T *var) +{ + return scoped_restore_tmpl<T> (var); +} + +/* Make a scoped_restore. This is useful because it lets template + argument deduction work. */ +template<typename T, typename T2> +scoped_restore_tmpl<T> make_scoped_restore (T *var, T2 value) +{ + return scoped_restore_tmpl<T> (var, value); +} + +#endif /* COMMON_SCOPED_RESTORE_H */ diff --git a/gdb/gdbsupport/selftest.c b/gdb/gdbsupport/selftest.c new file mode 100644 index 00000000000..f5a7b3c344b --- /dev/null +++ b/gdb/gdbsupport/selftest.c @@ -0,0 +1,111 @@ +/* GDB self-testing. + Copyright (C) 2016-2019 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 "common-exceptions.h" +#include "common-debug.h" +#include "selftest.h" +#include <map> + +namespace selftests +{ +/* All the tests that have been registered. Using an std::map allows keeping + the order of tests stable and easily looking up whether a test name + exists. */ + +static std::map<std::string, std::unique_ptr<selftest>> tests; + +/* A selftest that calls the test function without arguments. */ + +struct simple_selftest : public selftest +{ + simple_selftest (self_test_function *function_) + : function (function_) + {} + + void operator() () const override + { + function (); + } + + self_test_function *function; +}; + +/* See selftest.h. */ + +void +register_test (const std::string &name, selftest *test) +{ + /* Check that no test with this name already exist. */ + gdb_assert (tests.find (name) == tests.end ()); + + tests[name] = std::unique_ptr<selftest> (test); +} + +/* See selftest.h. */ + +void +register_test (const std::string &name, self_test_function *function) +{ + register_test (name, new simple_selftest (function)); +} + +/* See selftest.h. */ + +void +run_tests (const char *filter) +{ + int ran = 0, failed = 0; + + for (const auto &pair : tests) + { + const std::string &name = pair.first; + const std::unique_ptr<selftest> &test = pair.second; + + if (filter != NULL && *filter != '\0' + && name.find (filter) == std::string::npos) + continue; + + try + { + debug_printf (_("Running selftest %s.\n"), name.c_str ()); + ++ran; + (*test) (); + } + catch (const gdb_exception_error &ex) + { + ++failed; + debug_printf ("Self test failed: %s\n", ex.what ()); + } + + reset (); + } + + debug_printf (_("Ran %d unit tests, %d failed\n"), + ran, failed); +} + +/* See selftest.h. */ + +void for_each_selftest (for_each_selftest_ftype func) +{ + for (const auto &pair : tests) + func (pair.first); +} + +} // namespace selftests diff --git a/gdb/gdbsupport/selftest.h b/gdb/gdbsupport/selftest.h new file mode 100644 index 00000000000..c566ec5f3db --- /dev/null +++ b/gdb/gdbsupport/selftest.h @@ -0,0 +1,73 @@ +/* GDB self-testing. + Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_SELFTEST_H +#define COMMON_SELFTEST_H + +/* A test is just a function that does some checks and throws an + exception if something has gone wrong. */ + +typedef void self_test_function (void); + +namespace selftests +{ + +/* Interface for the various kinds of selftests. */ + +struct selftest +{ + virtual ~selftest () = default; + virtual void operator() () const = 0; +}; + +/* Register a new self-test. */ + +extern void register_test (const std::string &name, selftest *test); + +/* Register a new self-test. */ + +extern void register_test (const std::string &name, + self_test_function *function); + +/* Run all the self tests. This print a message describing the number + of test and the number of failures. + + If FILTER is not NULL and not empty, only tests with names containing FILTER + will be ran. */ + +extern void run_tests (const char *filter); + +/* Reset GDB or GDBserver's internal state. */ +extern void reset (); + +typedef void for_each_selftest_ftype (const std::string &name); + +/* Call FUNC for each registered selftest. */ + +extern void for_each_selftest (for_each_selftest_ftype func); +} + +/* Check that VALUE is true, and, if not, throw an exception. */ + +#define SELF_CHECK(VALUE) \ + do { \ + if (!(VALUE)) \ + error (_("self-test failed at %s:%d"), __FILE__, __LINE__); \ + } while (0) + +#endif /* COMMON_SELFTEST_H */ diff --git a/gdb/gdbsupport/signals-state-save-restore.c b/gdb/gdbsupport/signals-state-save-restore.c new file mode 100644 index 00000000000..c66d2ddc62f --- /dev/null +++ b/gdb/gdbsupport/signals-state-save-restore.c @@ -0,0 +1,117 @@ +/* Copyright (C) 2016-2019 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 "signals-state-save-restore.h" + +#include <signal.h> + +/* The original signal actions and mask. */ + +#ifdef HAVE_SIGACTION +static struct sigaction original_signal_actions[NSIG]; + +/* Note that we use sigprocmask without worrying about threads because + the save/restore functions are called either from main, or after a + fork. In both cases, we know the calling process is single + threaded. */ +static sigset_t original_signal_mask; +#endif + +/* See signals-state-save-restore.h. */ + +void +save_original_signals_state (bool quiet) +{ +#ifdef HAVE_SIGACTION + int i; + int res; + + res = sigprocmask (0, NULL, &original_signal_mask); + if (res == -1) + perror_with_name (("sigprocmask")); + + bool found_preinstalled = false; + + for (i = 1; i < NSIG; i++) + { + struct sigaction *oldact = &original_signal_actions[i]; + + res = sigaction (i, NULL, oldact); + if (res == -1 && errno == EINVAL) + { + /* Some signal numbers in the range are invalid. */ + continue; + } + else if (res == -1) + perror_with_name (("sigaction")); + + /* If we find a custom signal handler already installed, then + this function was called too late. This is a warning instead + of an internal error because this can also happen if you + LD_PRELOAD a library that installs a signal handler early via + __attribute__((constructor)), like libSegFault.so. */ + if (!quiet + && oldact->sa_handler != SIG_DFL + && oldact->sa_handler != SIG_IGN) + { + found_preinstalled = true; + + /* Use raw fprintf here because we're being called in early + startup, before GDB's filtered streams are created. */ + fprintf (stderr, + _("warning: Found custom handler for signal " + "%d (%s) preinstalled.\n"), i, + strsignal (i)); + } + } + + if (found_preinstalled) + { + fprintf (stderr, _("\ +Some signal dispositions inherited from the environment (SIG_DFL/SIG_IGN)\n\ +won't be propagated to spawned programs.\n")); + } +#endif +} + +/* See signals-state-save-restore.h. */ + +void +restore_original_signals_state (void) +{ +#ifdef HAVE_SIGACTION + int i; + int res; + + for (i = 1; i < NSIG; i++) + { + res = sigaction (i, &original_signal_actions[i], NULL); + if (res == -1 && errno == EINVAL) + { + /* Some signal numbers in the range are invalid. */ + continue; + } + else if (res == -1) + perror_with_name (("sigaction")); + } + + res = sigprocmask (SIG_SETMASK, &original_signal_mask, NULL); + if (res == -1) + perror_with_name (("sigprocmask")); +#endif +} diff --git a/gdb/gdbsupport/signals-state-save-restore.h b/gdb/gdbsupport/signals-state-save-restore.h new file mode 100644 index 00000000000..de3d896621b --- /dev/null +++ b/gdb/gdbsupport/signals-state-save-restore.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2016-2019 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/>. */ + +#ifndef COMMON_SIGNALS_STATE_SAVE_RESTORE_H +#define COMMON_SIGNALS_STATE_SAVE_RESTORE_H + +/* Save/restore the signal actions of all signals, and the signal + mask. + + Since the exec family of functions does not reset the signal + disposition of signals set to SIG_IGN, nor does it reset the signal + mask, in order to be transparent, when spawning new child processes + to debug (with "run", etc.), we must reset signal actions and mask + back to what was originally inherited from gdb/gdbserver's parent, + just before execing the target program to debug. */ + +/* Save the signal state of all signals. If !QUIET, warn if we detect + a custom signal handler preinstalled. */ + +extern void save_original_signals_state (bool quiet); + +/* Restore the signal state of all signals. */ + +extern void restore_original_signals_state (void); + +#endif /* COMMON_SIGNALS_STATE_SAVE_RESTORE_H */ diff --git a/gdb/gdbsupport/signals.c b/gdb/gdbsupport/signals.c new file mode 100644 index 00000000000..707bbe9fa3b --- /dev/null +++ b/gdb/gdbsupport/signals.c @@ -0,0 +1,653 @@ +/* Target signal translation functions for GDB. + Copyright (C) 1990-2019 Free Software Foundation, Inc. + Contributed by Cygnus Support. + + 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" + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#include "gdb_signals.h" + +struct gdbarch; + +/* Always use __SIGRTMIN if it's available. SIGRTMIN is the lowest + _available_ realtime signal, not the lowest supported; glibc takes + several for its own use. */ + +#ifndef REALTIME_LO +# if defined(__SIGRTMIN) +# define REALTIME_LO __SIGRTMIN +# define REALTIME_HI (__SIGRTMAX + 1) +# elif defined(SIGRTMIN) +# define REALTIME_LO SIGRTMIN +# define REALTIME_HI (SIGRTMAX + 1) +# endif +#endif + +/* This table must match in order and size the signals in enum + gdb_signal. */ + +static const struct { + const char *symbol; + const char *name; + const char *string; + } signals [] = +{ +#define SET(symbol, constant, name, string) { #symbol, name, string }, +#include "gdb/signals.def" +#undef SET +}; + +const char * +gdb_signal_to_symbol_string (enum gdb_signal sig) +{ + gdb_assert ((int) sig >= GDB_SIGNAL_FIRST && (int) sig <= GDB_SIGNAL_LAST); + + return signals[sig].symbol; +} + +/* Return the string for a signal. */ +const char * +gdb_signal_to_string (enum gdb_signal sig) +{ + if ((int) sig >= GDB_SIGNAL_FIRST && (int) sig <= GDB_SIGNAL_LAST) + return signals[sig].string; + else + return signals[GDB_SIGNAL_UNKNOWN].string; +} + +/* Return the name for a signal. */ +const char * +gdb_signal_to_name (enum gdb_signal sig) +{ + if ((int) sig >= GDB_SIGNAL_FIRST && (int) sig <= GDB_SIGNAL_LAST + && signals[sig].name != NULL) + return signals[sig].name; + else + /* I think the code which prints this will always print it along + with the string, so no need to be verbose (very old comment). */ + return "?"; +} + +/* Given a name, return its signal. */ +enum gdb_signal +gdb_signal_from_name (const char *name) +{ + enum gdb_signal sig; + + /* It's possible we also should allow "SIGCLD" as well as "SIGCHLD" + for GDB_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more + questionable; seems like by now people should call it SIGABRT + instead. */ + + /* This ugly cast brought to you by the native VAX compiler. */ + for (sig = GDB_SIGNAL_HUP; + sig < GDB_SIGNAL_LAST; + sig = (enum gdb_signal) ((int) sig + 1)) + if (signals[sig].name != NULL + && strcmp (name, signals[sig].name) == 0) + return sig; + return GDB_SIGNAL_UNKNOWN; +} + +/* The following functions are to help certain targets deal + with the signal/waitstatus stuff. They could just as well be in + a file called native-utils.c or unixwaitstatus-utils.c or whatever. */ + +/* Convert host signal to our signals. */ +enum gdb_signal +gdb_signal_from_host (int hostsig) +{ + /* A switch statement would make sense but would require special + kludges to deal with the cases where more than one signal has the + same number. Signals are ordered ANSI-standard signals first, + other signals second, with signals in each block ordered by their + numerical values on a typical POSIX platform. */ + + if (hostsig == 0) + return GDB_SIGNAL_0; + + /* SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV and SIGTERM + are ANSI-standard signals and are always available. */ + if (hostsig == SIGINT) + return GDB_SIGNAL_INT; + if (hostsig == SIGILL) + return GDB_SIGNAL_ILL; + if (hostsig == SIGABRT) + return GDB_SIGNAL_ABRT; + if (hostsig == SIGFPE) + return GDB_SIGNAL_FPE; + if (hostsig == SIGSEGV) + return GDB_SIGNAL_SEGV; + if (hostsig == SIGTERM) + return GDB_SIGNAL_TERM; + + /* All other signals need preprocessor conditionals. */ +#if defined (SIGHUP) + if (hostsig == SIGHUP) + return GDB_SIGNAL_HUP; +#endif +#if defined (SIGQUIT) + if (hostsig == SIGQUIT) + return GDB_SIGNAL_QUIT; +#endif +#if defined (SIGTRAP) + if (hostsig == SIGTRAP) + return GDB_SIGNAL_TRAP; +#endif +#if defined (SIGEMT) + if (hostsig == SIGEMT) + return GDB_SIGNAL_EMT; +#endif +#if defined (SIGKILL) + if (hostsig == SIGKILL) + return GDB_SIGNAL_KILL; +#endif +#if defined (SIGBUS) + if (hostsig == SIGBUS) + return GDB_SIGNAL_BUS; +#endif +#if defined (SIGSYS) + if (hostsig == SIGSYS) + return GDB_SIGNAL_SYS; +#endif +#if defined (SIGPIPE) + if (hostsig == SIGPIPE) + return GDB_SIGNAL_PIPE; +#endif +#if defined (SIGALRM) + if (hostsig == SIGALRM) + return GDB_SIGNAL_ALRM; +#endif +#if defined (SIGUSR1) + if (hostsig == SIGUSR1) + return GDB_SIGNAL_USR1; +#endif +#if defined (SIGUSR2) + if (hostsig == SIGUSR2) + return GDB_SIGNAL_USR2; +#endif +#if defined (SIGCLD) + if (hostsig == SIGCLD) + return GDB_SIGNAL_CHLD; +#endif +#if defined (SIGCHLD) + if (hostsig == SIGCHLD) + return GDB_SIGNAL_CHLD; +#endif +#if defined (SIGPWR) + if (hostsig == SIGPWR) + return GDB_SIGNAL_PWR; +#endif +#if defined (SIGWINCH) + if (hostsig == SIGWINCH) + return GDB_SIGNAL_WINCH; +#endif +#if defined (SIGURG) + if (hostsig == SIGURG) + return GDB_SIGNAL_URG; +#endif +#if defined (SIGIO) + if (hostsig == SIGIO) + return GDB_SIGNAL_IO; +#endif +#if defined (SIGPOLL) + if (hostsig == SIGPOLL) + return GDB_SIGNAL_POLL; +#endif +#if defined (SIGSTOP) + if (hostsig == SIGSTOP) + return GDB_SIGNAL_STOP; +#endif +#if defined (SIGTSTP) + if (hostsig == SIGTSTP) + return GDB_SIGNAL_TSTP; +#endif +#if defined (SIGCONT) + if (hostsig == SIGCONT) + return GDB_SIGNAL_CONT; +#endif +#if defined (SIGTTIN) + if (hostsig == SIGTTIN) + return GDB_SIGNAL_TTIN; +#endif +#if defined (SIGTTOU) + if (hostsig == SIGTTOU) + return GDB_SIGNAL_TTOU; +#endif +#if defined (SIGVTALRM) + if (hostsig == SIGVTALRM) + return GDB_SIGNAL_VTALRM; +#endif +#if defined (SIGPROF) + if (hostsig == SIGPROF) + return GDB_SIGNAL_PROF; +#endif +#if defined (SIGXCPU) + if (hostsig == SIGXCPU) + return GDB_SIGNAL_XCPU; +#endif +#if defined (SIGXFSZ) + if (hostsig == SIGXFSZ) + return GDB_SIGNAL_XFSZ; +#endif +#if defined (SIGWIND) + if (hostsig == SIGWIND) + return GDB_SIGNAL_WIND; +#endif +#if defined (SIGPHONE) + if (hostsig == SIGPHONE) + return GDB_SIGNAL_PHONE; +#endif +#if defined (SIGLOST) + if (hostsig == SIGLOST) + return GDB_SIGNAL_LOST; +#endif +#if defined (SIGWAITING) + if (hostsig == SIGWAITING) + return GDB_SIGNAL_WAITING; +#endif +#if defined (SIGCANCEL) + if (hostsig == SIGCANCEL) + return GDB_SIGNAL_CANCEL; +#endif +#if defined (SIGLWP) + if (hostsig == SIGLWP) + return GDB_SIGNAL_LWP; +#endif +#if defined (SIGDANGER) + if (hostsig == SIGDANGER) + return GDB_SIGNAL_DANGER; +#endif +#if defined (SIGGRANT) + if (hostsig == SIGGRANT) + return GDB_SIGNAL_GRANT; +#endif +#if defined (SIGRETRACT) + if (hostsig == SIGRETRACT) + return GDB_SIGNAL_RETRACT; +#endif +#if defined (SIGMSG) + if (hostsig == SIGMSG) + return GDB_SIGNAL_MSG; +#endif +#if defined (SIGSOUND) + if (hostsig == SIGSOUND) + return GDB_SIGNAL_SOUND; +#endif +#if defined (SIGSAK) + if (hostsig == SIGSAK) + return GDB_SIGNAL_SAK; +#endif +#if defined (SIGPRIO) + if (hostsig == SIGPRIO) + return GDB_SIGNAL_PRIO; +#endif + + /* Mach exceptions. Assumes that the values for EXC_ are positive! */ +#if defined (EXC_BAD_ACCESS) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BAD_ACCESS) + return GDB_EXC_BAD_ACCESS; +#endif +#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BAD_INSTRUCTION) + return GDB_EXC_BAD_INSTRUCTION; +#endif +#if defined (EXC_ARITHMETIC) && defined (_NSIG) + if (hostsig == _NSIG + EXC_ARITHMETIC) + return GDB_EXC_ARITHMETIC; +#endif +#if defined (EXC_EMULATION) && defined (_NSIG) + if (hostsig == _NSIG + EXC_EMULATION) + return GDB_EXC_EMULATION; +#endif +#if defined (EXC_SOFTWARE) && defined (_NSIG) + if (hostsig == _NSIG + EXC_SOFTWARE) + return GDB_EXC_SOFTWARE; +#endif +#if defined (EXC_BREAKPOINT) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BREAKPOINT) + return GDB_EXC_BREAKPOINT; +#endif + +#if defined (SIGINFO) + if (hostsig == SIGINFO) + return GDB_SIGNAL_INFO; +#endif +#if defined (SIGLIBRT) + if (hostsig == SIGLIBRT) + return GDB_SIGNAL_LIBRT; +#endif + +#if defined (REALTIME_LO) + if (hostsig >= REALTIME_LO && hostsig < REALTIME_HI) + { + /* This block of GDB_SIGNAL_REALTIME value is in order. */ + if (33 <= hostsig && hostsig <= 63) + return (enum gdb_signal) + (hostsig - 33 + (int) GDB_SIGNAL_REALTIME_33); + else if (hostsig == 32) + return GDB_SIGNAL_REALTIME_32; + else if (64 <= hostsig && hostsig <= 127) + return (enum gdb_signal) + (hostsig - 64 + (int) GDB_SIGNAL_REALTIME_64); + else + error (_("GDB bug: target.c (gdb_signal_from_host): " + "unrecognized real-time signal")); + } +#endif + + return GDB_SIGNAL_UNKNOWN; +} + +/* Convert a OURSIG (an enum gdb_signal) to the form used by the + target operating system (refered to as the ``host'') or zero if the + equivalent host signal is not available. Set/clear OURSIG_OK + accordingly. */ + +static int +do_gdb_signal_to_host (enum gdb_signal oursig, + int *oursig_ok) +{ + int retsig; + /* Silence the 'not used' warning, for targets that + do not support signals. */ + (void) retsig; + + /* Signals are ordered ANSI-standard signals first, other signals + second, with signals in each block ordered by their numerical + values on a typical POSIX platform. */ + + *oursig_ok = 1; + switch (oursig) + { + case GDB_SIGNAL_0: + return 0; + + /* SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV and SIGTERM + are ANSI-standard signals and are always available. */ + case GDB_SIGNAL_INT: + return SIGINT; + case GDB_SIGNAL_ILL: + return SIGILL; + case GDB_SIGNAL_ABRT: + return SIGABRT; + case GDB_SIGNAL_FPE: + return SIGFPE; + case GDB_SIGNAL_SEGV: + return SIGSEGV; + case GDB_SIGNAL_TERM: + return SIGTERM; + + /* All other signals need preprocessor conditionals. */ +#if defined (SIGHUP) + case GDB_SIGNAL_HUP: + return SIGHUP; +#endif +#if defined (SIGQUIT) + case GDB_SIGNAL_QUIT: + return SIGQUIT; +#endif +#if defined (SIGTRAP) + case GDB_SIGNAL_TRAP: + return SIGTRAP; +#endif +#if defined (SIGEMT) + case GDB_SIGNAL_EMT: + return SIGEMT; +#endif +#if defined (SIGKILL) + case GDB_SIGNAL_KILL: + return SIGKILL; +#endif +#if defined (SIGBUS) + case GDB_SIGNAL_BUS: + return SIGBUS; +#endif +#if defined (SIGSYS) + case GDB_SIGNAL_SYS: + return SIGSYS; +#endif +#if defined (SIGPIPE) + case GDB_SIGNAL_PIPE: + return SIGPIPE; +#endif +#if defined (SIGALRM) + case GDB_SIGNAL_ALRM: + return SIGALRM; +#endif +#if defined (SIGUSR1) + case GDB_SIGNAL_USR1: + return SIGUSR1; +#endif +#if defined (SIGUSR2) + case GDB_SIGNAL_USR2: + return SIGUSR2; +#endif +#if defined (SIGCHLD) || defined (SIGCLD) + case GDB_SIGNAL_CHLD: +#if defined (SIGCHLD) + return SIGCHLD; +#else + return SIGCLD; +#endif +#endif /* SIGCLD or SIGCHLD */ +#if defined (SIGPWR) + case GDB_SIGNAL_PWR: + return SIGPWR; +#endif +#if defined (SIGWINCH) + case GDB_SIGNAL_WINCH: + return SIGWINCH; +#endif +#if defined (SIGURG) + case GDB_SIGNAL_URG: + return SIGURG; +#endif +#if defined (SIGIO) + case GDB_SIGNAL_IO: + return SIGIO; +#endif +#if defined (SIGPOLL) + case GDB_SIGNAL_POLL: + return SIGPOLL; +#endif +#if defined (SIGSTOP) + case GDB_SIGNAL_STOP: + return SIGSTOP; +#endif +#if defined (SIGTSTP) + case GDB_SIGNAL_TSTP: + return SIGTSTP; +#endif +#if defined (SIGCONT) + case GDB_SIGNAL_CONT: + return SIGCONT; +#endif +#if defined (SIGTTIN) + case GDB_SIGNAL_TTIN: + return SIGTTIN; +#endif +#if defined (SIGTTOU) + case GDB_SIGNAL_TTOU: + return SIGTTOU; +#endif +#if defined (SIGVTALRM) + case GDB_SIGNAL_VTALRM: + return SIGVTALRM; +#endif +#if defined (SIGPROF) + case GDB_SIGNAL_PROF: + return SIGPROF; +#endif +#if defined (SIGXCPU) + case GDB_SIGNAL_XCPU: + return SIGXCPU; +#endif +#if defined (SIGXFSZ) + case GDB_SIGNAL_XFSZ: + return SIGXFSZ; +#endif +#if defined (SIGWIND) + case GDB_SIGNAL_WIND: + return SIGWIND; +#endif +#if defined (SIGPHONE) + case GDB_SIGNAL_PHONE: + return SIGPHONE; +#endif +#if defined (SIGLOST) + case GDB_SIGNAL_LOST: + return SIGLOST; +#endif +#if defined (SIGWAITING) + case GDB_SIGNAL_WAITING: + return SIGWAITING; +#endif +#if defined (SIGCANCEL) + case GDB_SIGNAL_CANCEL: + return SIGCANCEL; +#endif +#if defined (SIGLWP) + case GDB_SIGNAL_LWP: + return SIGLWP; +#endif +#if defined (SIGDANGER) + case GDB_SIGNAL_DANGER: + return SIGDANGER; +#endif +#if defined (SIGGRANT) + case GDB_SIGNAL_GRANT: + return SIGGRANT; +#endif +#if defined (SIGRETRACT) + case GDB_SIGNAL_RETRACT: + return SIGRETRACT; +#endif +#if defined (SIGMSG) + case GDB_SIGNAL_MSG: + return SIGMSG; +#endif +#if defined (SIGSOUND) + case GDB_SIGNAL_SOUND: + return SIGSOUND; +#endif +#if defined (SIGSAK) + case GDB_SIGNAL_SAK: + return SIGSAK; +#endif +#if defined (SIGPRIO) + case GDB_SIGNAL_PRIO: + return SIGPRIO; +#endif + + /* Mach exceptions. Assumes that the values for EXC_ are positive! */ +#if defined (EXC_BAD_ACCESS) && defined (_NSIG) + case GDB_EXC_BAD_ACCESS: + return _NSIG + EXC_BAD_ACCESS; +#endif +#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG) + case GDB_EXC_BAD_INSTRUCTION: + return _NSIG + EXC_BAD_INSTRUCTION; +#endif +#if defined (EXC_ARITHMETIC) && defined (_NSIG) + case GDB_EXC_ARITHMETIC: + return _NSIG + EXC_ARITHMETIC; +#endif +#if defined (EXC_EMULATION) && defined (_NSIG) + case GDB_EXC_EMULATION: + return _NSIG + EXC_EMULATION; +#endif +#if defined (EXC_SOFTWARE) && defined (_NSIG) + case GDB_EXC_SOFTWARE: + return _NSIG + EXC_SOFTWARE; +#endif +#if defined (EXC_BREAKPOINT) && defined (_NSIG) + case GDB_EXC_BREAKPOINT: + return _NSIG + EXC_BREAKPOINT; +#endif + +#if defined (SIGINFO) + case GDB_SIGNAL_INFO: + return SIGINFO; +#endif +#if defined (SIGLIBRT) + case GDB_SIGNAL_LIBRT: + return SIGLIBRT; +#endif + + default: +#if defined (REALTIME_LO) + retsig = 0; + + if (oursig >= GDB_SIGNAL_REALTIME_33 + && oursig <= GDB_SIGNAL_REALTIME_63) + { + /* This block of signals is continuous, and + GDB_SIGNAL_REALTIME_33 is 33 by definition. */ + retsig = (int) oursig - (int) GDB_SIGNAL_REALTIME_33 + 33; + } + else if (oursig == GDB_SIGNAL_REALTIME_32) + { + /* GDB_SIGNAL_REALTIME_32 isn't contiguous with + GDB_SIGNAL_REALTIME_33. It is 32 by definition. */ + retsig = 32; + } + else if (oursig >= GDB_SIGNAL_REALTIME_64 + && oursig <= GDB_SIGNAL_REALTIME_127) + { + /* This block of signals is continuous, and + GDB_SIGNAL_REALTIME_64 is 64 by definition. */ + retsig = (int) oursig - (int) GDB_SIGNAL_REALTIME_64 + 64; + } + + if (retsig >= REALTIME_LO && retsig < REALTIME_HI) + return retsig; +#endif + + *oursig_ok = 0; + return 0; + } +} + +int +gdb_signal_to_host_p (enum gdb_signal oursig) +{ + int oursig_ok; + do_gdb_signal_to_host (oursig, &oursig_ok); + return oursig_ok; +} + +int +gdb_signal_to_host (enum gdb_signal oursig) +{ + int oursig_ok; + int targ_signo = do_gdb_signal_to_host (oursig, &oursig_ok); + if (!oursig_ok) + { + /* The user might be trying to do "signal SIGSAK" where this system + doesn't have SIGSAK. */ + warning (_("Signal %s does not exist on this system."), + gdb_signal_to_name (oursig)); + return 0; + } + else + return targ_signo; +} diff --git a/gdb/gdbsupport/symbol.h b/gdb/gdbsupport/symbol.h new file mode 100644 index 00000000000..a42d50a194c --- /dev/null +++ b/gdb/gdbsupport/symbol.h @@ -0,0 +1,37 @@ +/* Declarations of common symbol functions. + + Copyright (C) 2014-2019 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/>. */ + +#ifndef COMMON_SYMBOL_H +#define COMMON_SYMBOL_H + +struct objfile; + +/* Find a symbol that matches NAME. Limit the search to OBJFILE if + OBJFILE is non-NULL and the implementation supports limiting the + search to specific object files. NAME may be mangled or demangled. + If a match is found, store the matching symbol's address in ADDR + and return zero. Returns nonzero if no symbol matching NAME is + found. Raise an exception if OBJFILE is non-NULL and the + implementation does not support limiting searches to specific + object files. This function must be provided by the client. */ + +extern int find_minimal_symbol_address (const char *name, CORE_ADDR *addr, + struct objfile *objfile); + +#endif /* COMMON_SYMBOL_H */ diff --git a/gdb/gdbsupport/tdesc.c b/gdb/gdbsupport/tdesc.c new file mode 100644 index 00000000000..1e2a3148132 --- /dev/null +++ b/gdb/gdbsupport/tdesc.c @@ -0,0 +1,401 @@ +/* Target description support for GDB. + + Copyright (C) 2018-2019 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 "gdbsupport/tdesc.h" + +tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_, + int regnum, int save_restore_, const char *group_, + int bitsize_, const char *type_) + : name (name_), target_regnum (regnum), + save_restore (save_restore_), + group (group_ != NULL ? group_ : ""), + bitsize (bitsize_), + type (type_ != NULL ? type_ : "<unknown>") +{ + /* If the register's type is target-defined, look it up now. We may not + have easy access to the containing feature when we want it later. */ + tdesc_type = tdesc_named_type (feature, type.c_str ()); +} + +/* Predefined types. */ +static tdesc_type_builtin tdesc_predefined_types[] = +{ + { "bool", TDESC_TYPE_BOOL }, + { "int8", TDESC_TYPE_INT8 }, + { "int16", TDESC_TYPE_INT16 }, + { "int32", TDESC_TYPE_INT32 }, + { "int64", TDESC_TYPE_INT64 }, + { "int128", TDESC_TYPE_INT128 }, + { "uint8", TDESC_TYPE_UINT8 }, + { "uint16", TDESC_TYPE_UINT16 }, + { "uint32", TDESC_TYPE_UINT32 }, + { "uint64", TDESC_TYPE_UINT64 }, + { "uint128", TDESC_TYPE_UINT128 }, + { "code_ptr", TDESC_TYPE_CODE_PTR }, + { "data_ptr", TDESC_TYPE_DATA_PTR }, + { "ieee_half", TDESC_TYPE_IEEE_HALF }, + { "ieee_single", TDESC_TYPE_IEEE_SINGLE }, + { "ieee_double", TDESC_TYPE_IEEE_DOUBLE }, + { "arm_fpa_ext", TDESC_TYPE_ARM_FPA_EXT }, + { "i387_ext", TDESC_TYPE_I387_EXT } +}; + +void tdesc_feature::accept (tdesc_element_visitor &v) const +{ + v.visit_pre (this); + + for (const tdesc_type_up &type : types) + type->accept (v); + + for (const tdesc_reg_up ® : registers) + reg->accept (v); + + v.visit_post (this); +} + +bool tdesc_feature::operator== (const tdesc_feature &other) const +{ + if (name != other.name) + return false; + + if (registers.size () != other.registers.size ()) + return false; + + for (int ix = 0; ix < registers.size (); ix++) + { + const tdesc_reg_up ®1 = registers[ix]; + const tdesc_reg_up ®2 = other.registers[ix]; + + if (reg1 != reg2 && *reg1 != *reg2) + return false; + } + + if (types.size () != other.types.size ()) + return false; + + for (int ix = 0; ix < types.size (); ix++) + { + const tdesc_type_up &type1 = types[ix]; + const tdesc_type_up &type2 = other.types[ix]; + + if (type1 != type2 && *type1 != *type2) + return false; + } + + return true; +} + +/* Lookup a predefined type. */ + +static struct tdesc_type * +tdesc_predefined_type (enum tdesc_type_kind kind) +{ + for (int ix = 0; ix < ARRAY_SIZE (tdesc_predefined_types); ix++) + if (tdesc_predefined_types[ix].kind == kind) + return &tdesc_predefined_types[ix]; + + gdb_assert_not_reached ("bad predefined tdesc type"); +} + +/* See gdbsupport/tdesc.h. */ + +struct tdesc_type * +tdesc_named_type (const struct tdesc_feature *feature, const char *id) +{ + /* First try target-defined types. */ + for (const tdesc_type_up &type : feature->types) + if (type->name == id) + return type.get (); + + /* Next try the predefined types. */ + for (int ix = 0; ix < ARRAY_SIZE (tdesc_predefined_types); ix++) + if (tdesc_predefined_types[ix].name == id) + return &tdesc_predefined_types[ix]; + + return NULL; +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_create_reg (struct tdesc_feature *feature, const char *name, + int regnum, int save_restore, const char *group, + int bitsize, const char *type) +{ + tdesc_reg *reg = new tdesc_reg (feature, name, regnum, save_restore, + group, bitsize, type); + + feature->registers.emplace_back (reg); +} + +/* See gdbsupport/tdesc.h. */ + +struct tdesc_type * +tdesc_create_vector (struct tdesc_feature *feature, const char *name, + struct tdesc_type *field_type, int count) +{ + tdesc_type_vector *type = new tdesc_type_vector (name, field_type, count); + feature->types.emplace_back (type); + + return type; +} + +/* See gdbsupport/tdesc.h. */ + +tdesc_type_with_fields * +tdesc_create_struct (struct tdesc_feature *feature, const char *name) +{ + tdesc_type_with_fields *type + = new tdesc_type_with_fields (name, TDESC_TYPE_STRUCT); + feature->types.emplace_back (type); + + return type; +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_set_struct_size (tdesc_type_with_fields *type, int size) +{ + gdb_assert (type->kind == TDESC_TYPE_STRUCT); + gdb_assert (size > 0); + type->size = size; +} + +/* See gdbsupport/tdesc.h. */ + +tdesc_type_with_fields * +tdesc_create_union (struct tdesc_feature *feature, const char *name) +{ + tdesc_type_with_fields *type + = new tdesc_type_with_fields (name, TDESC_TYPE_UNION); + feature->types.emplace_back (type); + + return type; +} + +/* See gdbsupport/tdesc.h. */ + +tdesc_type_with_fields * +tdesc_create_flags (struct tdesc_feature *feature, const char *name, + int size) +{ + gdb_assert (size > 0); + + tdesc_type_with_fields *type + = new tdesc_type_with_fields (name, TDESC_TYPE_FLAGS, size); + feature->types.emplace_back (type); + + return type; +} + +/* See gdbsupport/tdesc.h. */ + +tdesc_type_with_fields * +tdesc_create_enum (struct tdesc_feature *feature, const char *name, + int size) +{ + gdb_assert (size > 0); + + tdesc_type_with_fields *type + = new tdesc_type_with_fields (name, TDESC_TYPE_ENUM, size); + feature->types.emplace_back (type); + + return type; +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_add_field (tdesc_type_with_fields *type, const char *field_name, + struct tdesc_type *field_type) +{ + gdb_assert (type->kind == TDESC_TYPE_UNION + || type->kind == TDESC_TYPE_STRUCT); + + /* Initialize start and end so we know this is not a bit-field + when we print-c-tdesc. */ + type->fields.emplace_back (field_name, field_type, -1, -1); +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_add_typed_bitfield (tdesc_type_with_fields *type, const char *field_name, + int start, int end, struct tdesc_type *field_type) +{ + gdb_assert (type->kind == TDESC_TYPE_STRUCT + || type->kind == TDESC_TYPE_FLAGS); + gdb_assert (start >= 0 && end >= start); + + type->fields.emplace_back (field_name, field_type, start, end); +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_add_bitfield (tdesc_type_with_fields *type, const char *field_name, + int start, int end) +{ + struct tdesc_type *field_type; + + gdb_assert (start >= 0 && end >= start); + + if (type->size > 4) + field_type = tdesc_predefined_type (TDESC_TYPE_UINT64); + else + field_type = tdesc_predefined_type (TDESC_TYPE_UINT32); + + tdesc_add_typed_bitfield (type, field_name, start, end, field_type); +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_add_flag (tdesc_type_with_fields *type, int start, + const char *flag_name) +{ + gdb_assert (type->kind == TDESC_TYPE_FLAGS + || type->kind == TDESC_TYPE_STRUCT); + + type->fields.emplace_back (flag_name, + tdesc_predefined_type (TDESC_TYPE_BOOL), + start, start); +} + +/* See gdbsupport/tdesc.h. */ + +void +tdesc_add_enum_value (tdesc_type_with_fields *type, int value, + const char *name) +{ + gdb_assert (type->kind == TDESC_TYPE_ENUM); + type->fields.emplace_back (name, + tdesc_predefined_type (TDESC_TYPE_INT32), + value, -1); +} + +void print_xml_feature::visit_pre (const tdesc_feature *e) +{ + string_appendf (*m_buffer, "<feature name=\"%s\">\n", e->name.c_str ()); +} + +void print_xml_feature::visit_post (const tdesc_feature *e) +{ + string_appendf (*m_buffer, "</feature>\n"); +} + +void print_xml_feature::visit (const tdesc_type_builtin *t) +{ + error (_("xml output is not supported for type \"%s\"."), t->name.c_str ()); +} + +void print_xml_feature::visit (const tdesc_type_vector *t) +{ + string_appendf (*m_buffer, "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n", + t->name.c_str (), t->element_type->name.c_str (), t->count); +} + +void print_xml_feature::visit (const tdesc_type_with_fields *t) +{ + const static char *types[] = { "struct", "union", "flags", "enum" }; + + gdb_assert (t->kind >= TDESC_TYPE_STRUCT && t->kind <= TDESC_TYPE_ENUM); + + string_appendf (*m_buffer, + "<%s id=\"%s\"", types[t->kind - TDESC_TYPE_STRUCT], + t->name.c_str ()); + + switch (t->kind) + { + case TDESC_TYPE_STRUCT: + case TDESC_TYPE_FLAGS: + if (t->size > 0) + string_appendf (*m_buffer, " size=\"%d\"", t->size); + string_appendf (*m_buffer, ">\n"); + + for (const tdesc_type_field &f : t->fields) + { + string_appendf (*m_buffer, " <field name=\"%s\" ", f.name.c_str ()); + if (f.start == -1) + string_appendf (*m_buffer, "type=\"%s\"/>\n", + f.type->name.c_str ()); + else + string_appendf (*m_buffer, "start=\"%d\" end=\"%d\"/>\n", f.start, + f.end); + } + break; + + case TDESC_TYPE_ENUM: + string_appendf (*m_buffer, ">\n"); + for (const tdesc_type_field &f : t->fields) + string_appendf (*m_buffer, " <field name=\"%s\" start=\"%d\"/>\n", + f.name.c_str (), f.start); + break; + + case TDESC_TYPE_UNION: + string_appendf (*m_buffer, ">\n"); + for (const tdesc_type_field &f : t->fields) + string_appendf (*m_buffer, " <field name=\"%s\" type=\"%s\"/>\n", + f.name.c_str (), f.type->name.c_str ()); + break; + + default: + error (_("xml output is not supported for type \"%s\"."), + t->name.c_str ()); + } + + string_appendf (*m_buffer, "</%s>\n", types[t->kind - TDESC_TYPE_STRUCT]); +} + +void print_xml_feature::visit (const tdesc_reg *r) +{ + string_appendf (*m_buffer, + "<reg name=\"%s\" bitsize=\"%d\" type=\"%s\" regnum=\"%ld\"", + r->name.c_str (), r->bitsize, r->type.c_str (), + r->target_regnum); + + if (r->group.length () > 0) + string_appendf (*m_buffer, " group=\"%s\"", r->group.c_str ()); + + if (r->save_restore == 0) + string_appendf (*m_buffer, " save-restore=\"no\""); + + string_appendf (*m_buffer, "/>\n"); +} + +void print_xml_feature::visit_pre (const target_desc *e) +{ +#ifndef IN_PROCESS_AGENT + string_appendf (*m_buffer, "<?xml version=\"1.0\"?>\n"); + string_appendf (*m_buffer, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"); + string_appendf (*m_buffer, "<target>\n<architecture>%s</architecture>\n", + tdesc_architecture_name (e)); + + const char *osabi = tdesc_osabi_name (e); + if (osabi != nullptr) + string_appendf (*m_buffer, "<osabi>%s</osabi>", osabi); +#endif +} + +void print_xml_feature::visit_post (const target_desc *e) +{ + string_appendf (*m_buffer, "</target>\n"); +} diff --git a/gdb/gdbsupport/tdesc.h b/gdb/gdbsupport/tdesc.h new file mode 100644 index 00000000000..9eb3d5ea312 --- /dev/null +++ b/gdb/gdbsupport/tdesc.h @@ -0,0 +1,408 @@ +/* Copyright (C) 2006-2019 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/>. */ + +#ifndef COMMON_TDESC_H +#define COMMON_TDESC_H + +struct tdesc_feature; +struct tdesc_type; +struct tdesc_type_builtin; +struct tdesc_type_vector; +struct tdesc_type_with_fields; +struct tdesc_reg; +struct target_desc; + +/* The interface to visit different elements of target description. */ + +class tdesc_element_visitor +{ +public: + virtual void visit_pre (const target_desc *e) + {} + + virtual void visit_post (const target_desc *e) + {} + + virtual void visit_pre (const tdesc_feature *e) + {} + + virtual void visit_post (const tdesc_feature *e) + {} + + virtual void visit (const tdesc_type_builtin *e) + {} + + virtual void visit (const tdesc_type_vector *e) + {} + + virtual void visit (const tdesc_type_with_fields *e) + {} + + virtual void visit (const tdesc_reg *e) + {} +}; + +class tdesc_element +{ +public: + virtual void accept (tdesc_element_visitor &v) const = 0; +}; + +/* An individual register from a target description. */ + +struct tdesc_reg : tdesc_element +{ + tdesc_reg (struct tdesc_feature *feature, const std::string &name_, + int regnum, int save_restore_, const char *group_, + int bitsize_, const char *type_); + + virtual ~tdesc_reg () = default; + + DISABLE_COPY_AND_ASSIGN (tdesc_reg); + + /* The name of this register. In standard features, it may be + recognized by the architecture support code, or it may be purely + for the user. */ + std::string name; + + /* The register number used by this target to refer to this + register. This is used for remote p/P packets and to determine + the ordering of registers in the remote g/G packets. */ + long target_regnum; + + /* If this flag is set, GDB should save and restore this register + around calls to an inferior function. */ + int save_restore; + + /* The name of the register group containing this register, or empty + if the group should be automatically determined from the + register's type. If this is "general", "float", or "vector", the + corresponding "info" command should display this register's + value. It can be an arbitrary string, but should be limited to + alphanumeric characters and internal hyphens. Currently other + strings are ignored (treated as empty). */ + std::string group; + + /* The size of the register, in bits. */ + int bitsize; + + /* The type of the register. This string corresponds to either + a named type from the target description or a predefined + type from GDB. */ + std::string type; + + /* The target-described type corresponding to TYPE, if found. */ + struct tdesc_type *tdesc_type; + + void accept (tdesc_element_visitor &v) const override + { + v.visit (this); + } + + bool operator== (const tdesc_reg &other) const + { + return (name == other.name + && target_regnum == other.target_regnum + && save_restore == other.save_restore + && bitsize == other.bitsize + && group == other.group + && type == other.type); + } + + bool operator!= (const tdesc_reg &other) const + { + return !(*this == other); + } +}; + +typedef std::unique_ptr<tdesc_reg> tdesc_reg_up; + +enum tdesc_type_kind +{ + /* Predefined types. */ + TDESC_TYPE_BOOL, + TDESC_TYPE_INT8, + TDESC_TYPE_INT16, + TDESC_TYPE_INT32, + TDESC_TYPE_INT64, + TDESC_TYPE_INT128, + TDESC_TYPE_UINT8, + TDESC_TYPE_UINT16, + TDESC_TYPE_UINT32, + TDESC_TYPE_UINT64, + TDESC_TYPE_UINT128, + TDESC_TYPE_CODE_PTR, + TDESC_TYPE_DATA_PTR, + TDESC_TYPE_IEEE_HALF, + TDESC_TYPE_IEEE_SINGLE, + TDESC_TYPE_IEEE_DOUBLE, + TDESC_TYPE_ARM_FPA_EXT, + TDESC_TYPE_I387_EXT, + + /* Types defined by a target feature. */ + TDESC_TYPE_VECTOR, + TDESC_TYPE_STRUCT, + TDESC_TYPE_UNION, + TDESC_TYPE_FLAGS, + TDESC_TYPE_ENUM +}; + +struct tdesc_type : tdesc_element +{ + tdesc_type (const std::string &name_, enum tdesc_type_kind kind_) + : name (name_), kind (kind_) + {} + + virtual ~tdesc_type () = default; + + DISABLE_COPY_AND_ASSIGN (tdesc_type); + + /* The name of this type. */ + std::string name; + + /* Identify the kind of this type. */ + enum tdesc_type_kind kind; + + bool operator== (const tdesc_type &other) const + { + return name == other.name && kind == other.kind; + } + + bool operator!= (const tdesc_type &other) const + { + return !(*this == other); + } +}; + +typedef std::unique_ptr<tdesc_type> tdesc_type_up; + +struct tdesc_type_builtin : tdesc_type +{ + tdesc_type_builtin (const std::string &name, enum tdesc_type_kind kind) + : tdesc_type (name, kind) + {} + + void accept (tdesc_element_visitor &v) const override + { + v.visit (this); + } +}; + +/* tdesc_type for vector types. */ + +struct tdesc_type_vector : tdesc_type +{ + tdesc_type_vector (const std::string &name, tdesc_type *element_type_, + int count_) + : tdesc_type (name, TDESC_TYPE_VECTOR), + element_type (element_type_), count (count_) + {} + + void accept (tdesc_element_visitor &v) const override + { + v.visit (this); + } + + struct tdesc_type *element_type; + int count; +}; + +/* A named type from a target description. */ + +struct tdesc_type_field +{ + tdesc_type_field (const std::string &name_, tdesc_type *type_, + int start_, int end_) + : name (name_), type (type_), start (start_), end (end_) + {} + + std::string name; + struct tdesc_type *type; + /* For non-enum-values, either both are -1 (non-bitfield), or both are + not -1 (bitfield). For enum values, start is the value (which could be + -1), end is -1. */ + int start, end; +}; + +/* tdesc_type for struct, union, flags, and enum types. */ + +struct tdesc_type_with_fields : tdesc_type +{ + tdesc_type_with_fields (const std::string &name, tdesc_type_kind kind, + int size_ = 0) + : tdesc_type (name, kind), size (size_) + {} + + void accept (tdesc_element_visitor &v) const override + { + v.visit (this); + } + + std::vector<tdesc_type_field> fields; + int size; +}; + +/* A feature from a target description. Each feature is a collection + of other elements, e.g. registers and types. */ + +struct tdesc_feature : tdesc_element +{ + tdesc_feature (const std::string &name_) + : name (name_) + {} + + virtual ~tdesc_feature () = default; + + DISABLE_COPY_AND_ASSIGN (tdesc_feature); + + /* The name of this feature. It may be recognized by the architecture + support code. */ + std::string name; + + /* The registers associated with this feature. */ + std::vector<tdesc_reg_up> registers; + + /* The types associated with this feature. */ + std::vector<tdesc_type_up> types; + + void accept (tdesc_element_visitor &v) const override; + + bool operator== (const tdesc_feature &other) const; + + bool operator!= (const tdesc_feature &other) const + { + return !(*this == other); + } +}; + +typedef std::unique_ptr<tdesc_feature> tdesc_feature_up; + +/* Allocate a new target_desc. */ +target_desc *allocate_target_description (void); + +/* Set TARGET_DESC's architecture by NAME. */ +void set_tdesc_architecture (target_desc *target_desc, + const char *name); + +/* Return the architecture associated with this target description as a string, + or NULL if no architecture was specified. */ +const char *tdesc_architecture_name (const struct target_desc *target_desc); + +/* Set TARGET_DESC's osabi by NAME. */ +void set_tdesc_osabi (target_desc *target_desc, const char *name); + +/* Return the osabi associated with this target description as a string, + or NULL if no osabi was specified. */ +const char *tdesc_osabi_name (const struct target_desc *target_desc); + +/* Return the type associated with ID in the context of FEATURE, or + NULL if none. */ +struct tdesc_type *tdesc_named_type (const struct tdesc_feature *feature, + const char *id); + +/* Return the created feature named NAME in target description TDESC. */ +struct tdesc_feature *tdesc_create_feature (struct target_desc *tdesc, + const char *name); + +/* Return the created vector tdesc_type named NAME in FEATURE. */ +struct tdesc_type *tdesc_create_vector (struct tdesc_feature *feature, + const char *name, + struct tdesc_type *field_type, + int count); + +/* Return the created struct tdesc_type named NAME in FEATURE. */ +tdesc_type_with_fields *tdesc_create_struct (struct tdesc_feature *feature, + const char *name); + +/* Return the created union tdesc_type named NAME in FEATURE. */ +tdesc_type_with_fields *tdesc_create_union (struct tdesc_feature *feature, + const char *name); + +/* Return the created flags tdesc_type named NAME in FEATURE. */ +tdesc_type_with_fields *tdesc_create_flags (struct tdesc_feature *feature, + const char *name, + int size); + +/* Return the created enum tdesc_type named NAME in FEATURE. */ +tdesc_type_with_fields *tdesc_create_enum (struct tdesc_feature *feature, + const char *name, + int size); + +/* Add a new field to TYPE. FIELD_NAME is its name, and FIELD_TYPE is + its type. */ +void tdesc_add_field (tdesc_type_with_fields *type, const char *field_name, + struct tdesc_type *field_type); + +/* Add a new bitfield to TYPE, with range START to END. FIELD_NAME is its name, + and FIELD_TYPE is its type. */ +void tdesc_add_typed_bitfield (tdesc_type_with_fields *type, + const char *field_name, + int start, int end, + struct tdesc_type *field_type); + +/* Set the total length of TYPE. Structs which contain bitfields may + omit the reserved bits, so the end of the last field may not + suffice. */ +void tdesc_set_struct_size (tdesc_type_with_fields *type, int size); + +/* Add a new untyped bitfield to TYPE. + Untyped bitfields become either uint32 or uint64 depending on the size + of the underlying type. */ +void tdesc_add_bitfield (tdesc_type_with_fields *type, const char *field_name, + int start, int end); + +/* A flag is just a typed(bool) single-bit bitfield. + This function is kept to minimize changes in generated files. */ +void tdesc_add_flag (tdesc_type_with_fields *type, int start, + const char *flag_name); + +/* Add field with VALUE and NAME to the enum TYPE. */ +void tdesc_add_enum_value (tdesc_type_with_fields *type, int value, + const char *name); + +/* Create a register in feature FEATURE. */ +void tdesc_create_reg (struct tdesc_feature *feature, const char *name, + int regnum, int save_restore, const char *group, + int bitsize, const char *type); + +/* Return the tdesc in string XML format. */ + +const char *tdesc_get_features_xml (const target_desc *tdesc); + +/* Print target description as xml. */ + +class print_xml_feature : public tdesc_element_visitor +{ +public: + print_xml_feature (std::string *buffer_) + : m_buffer (buffer_) + {} + + void visit_pre (const target_desc *e) override; + void visit_post (const target_desc *e) override; + void visit_pre (const tdesc_feature *e) override; + void visit_post (const tdesc_feature *e) override; + void visit (const tdesc_type_builtin *type) override; + void visit (const tdesc_type_vector *type) override; + void visit (const tdesc_type_with_fields *type) override; + void visit (const tdesc_reg *reg) override; + +private: + std::string *m_buffer; +}; + +#endif /* COMMON_TDESC_H */ diff --git a/gdb/gdbsupport/traits.h b/gdb/gdbsupport/traits.h new file mode 100644 index 00000000000..de57c1903cd --- /dev/null +++ b/gdb/gdbsupport/traits.h @@ -0,0 +1,109 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_TRAITS_H +#define COMMON_TRAITS_H + +#include <type_traits> + +/* GCC does not understand __has_feature. */ +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +/* HAVE_IS_TRIVIALLY_COPYABLE is defined as 1 iff + std::is_trivially_copyable is available. GCC only implemented it + in GCC 5. */ +#if (__has_feature(is_trivially_copyable) \ + || (defined __GNUC__ && __GNUC__ >= 5)) +# define HAVE_IS_TRIVIALLY_COPYABLE 1 +#endif + +/* HAVE_IS_TRIVIALLY_CONSTRUCTIBLE is defined as 1 iff + std::is_trivially_constructible is available. GCC only implemented it + in GCC 5. */ +#if (__has_feature(is_trivially_constructible) \ + || (defined __GNUC__ && __GNUC__ >= 5)) +# define HAVE_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#endif + +namespace gdb { + +/* Pre C++14-safe (CWG 1558) version of C++17's std::void_t. See + <http://en.cppreference.com/w/cpp/types/void_t>. */ + +template<typename... Ts> +struct make_void { typedef void type; }; + +template<typename... Ts> +using void_t = typename make_void<Ts...>::type; + +/* A few trait helpers, mainly stolen from libstdc++. Uppercase + because "and/or", etc. are reserved keywords. */ + +template<typename Predicate> +struct Not : public std::integral_constant<bool, !Predicate::value> +{}; + +template<typename...> +struct Or; + +template<> +struct Or<> : public std::false_type +{}; + +template<typename B1> +struct Or<B1> : public B1 +{}; + +template<typename B1, typename B2> +struct Or<B1, B2> + : public std::conditional<B1::value, B1, B2>::type +{}; + +template<typename B1,typename B2,typename B3, typename... Bn> +struct Or<B1, B2, B3, Bn...> + : public std::conditional<B1::value, B1, Or<B2, B3, Bn...>>::type +{}; + +template<typename...> +struct And; + +template<> +struct And<> : public std::true_type +{}; + +template<typename B1> +struct And<B1> : public B1 +{}; + +template<typename B1, typename B2> +struct And<B1, B2> + : public std::conditional<B1::value, B2, B1>::type +{}; + +template<typename B1, typename B2, typename B3, typename... Bn> +struct And<B1, B2, B3, Bn...> + : public std::conditional<B1::value, And<B2, B3, Bn...>, B1>::type +{}; + +/* Concepts-light-like helper to make SFINAE logic easier to read. */ +template<typename Condition> +using Requires = typename std::enable_if<Condition::value, void>::type; +} + +#endif /* COMMON_TRAITS_H */ diff --git a/gdb/gdbsupport/underlying.h b/gdb/gdbsupport/underlying.h new file mode 100644 index 00000000000..af14a3627d5 --- /dev/null +++ b/gdb/gdbsupport/underlying.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2017-2019 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/>. */ + +#ifndef COMMON_UNDERLYING_H +#define COMMON_UNDERLYING_H + +#include <type_traits> + +/* Convert an enum to its underlying value. */ + +template<typename E> +constexpr typename std::underlying_type<E>::type +to_underlying (E val) noexcept +{ + return static_cast<typename std::underlying_type<E>::type> (val); +} + +#endif diff --git a/gdb/gdbsupport/valid-expr.h b/gdb/gdbsupport/valid-expr.h new file mode 100644 index 00000000000..c622d3546e3 --- /dev/null +++ b/gdb/gdbsupport/valid-expr.h @@ -0,0 +1,108 @@ +/* Compile-time valid expression checker for GDB, the GNU debugger. + + Copyright (C) 2017-2019 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/>. */ + +/* Helper macros used to build compile-time unit tests that make sure + that invalid expressions that should not compile would not compile, + and that expressions that should compile do compile, and have the + right type. This is mainly used to verify that some utility's API + is really as safe as intended. */ + +#ifndef COMMON_VALID_EXPR_H +#define COMMON_VALID_EXPR_H + +#include "gdbsupport/preprocessor.h" +#include "gdbsupport/traits.h" + +/* Macro that uses SFINAE magic to detect whether the EXPR expression + is either valid or ill-formed, at compile time, without actually + producing compile-time errors. I.e., check that bad uses of the + types (e.g., involving mismatching types) would be caught at + compile time. If the expression is valid, also check whether the + expression has the right type. + + EXPR must be defined in terms of some of the template parameters, + so that template substitution failure discards the overload instead + of causing a real compile error. TYPES is thus the list of types + involved in the expression, and TYPENAMES is the same list, but + with each element prefixed by "typename". These are passed as + template parameter types to the templates within the macro. + + VALID is a boolean that indicates whether the expression is + supposed to be valid or invalid. + + EXPR_TYPE is the expected type of EXPR. Only meaningful iff VALID + is true. If VALID is false, then you must pass "void" as expected + type. + + Each invocation of the macro is wrapped in its own namespace to + avoid ODR violations. The generated namespace only includes the + line number, so client code should wrap sets of calls in a + test-specific namespace too, to fully guarantee uniqueness between + the multiple clients in the codebase. */ +#define CHECK_VALID_EXPR_INT(TYPENAMES, TYPES, VALID, EXPR_TYPE, EXPR) \ + namespace CONCAT (check_valid_expr, __LINE__) { \ + \ + template<typename, typename, typename = void> \ + struct is_valid_expression \ + : std::false_type {}; \ + \ + template <TYPENAMES> \ + struct is_valid_expression<TYPES, gdb::void_t<decltype (EXPR)>> \ + : std::true_type {}; \ + \ + static_assert (is_valid_expression<TYPES>::value == VALID, \ + ""); \ + \ + template<TYPENAMES, typename = void> \ + struct is_same_type \ + : std::is_same<EXPR_TYPE, void> {}; \ + \ + template <TYPENAMES> \ + struct is_same_type<TYPES, gdb::void_t<decltype (EXPR)>> \ + : std::is_same<EXPR_TYPE, decltype (EXPR)> {}; \ + \ + static_assert (is_same_type<TYPES>::value, ""); \ + } /* namespace */ + +/* A few convenience macros that support expressions involving a + varying numbers of types. If you need more types, feel free to add + another variant. */ + +#define CHECK_VALID_EXPR_1(T1, VALID, EXPR_TYPE, EXPR) \ + CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1), \ + ESC_PARENS (T1), \ + VALID, EXPR_TYPE, EXPR) + +#define CHECK_VALID_EXPR_2(T1, T2, VALID, EXPR_TYPE, EXPR) \ + CHECK_VALID_EXPR_INT (ESC_PARENS(typename T1, typename T2), \ + ESC_PARENS (T1, T2), \ + VALID, EXPR_TYPE, EXPR) + +#define CHECK_VALID_EXPR_3(T1, T2, T3, VALID, EXPR_TYPE, EXPR) \ + CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, typename T3), \ + ESC_PARENS (T1, T2, T3), \ + VALID, EXPR_TYPE, EXPR) + +#define CHECK_VALID_EXPR_4(T1, T2, T3, T4, VALID, EXPR_TYPE, EXPR) \ + CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \ + typename T3, typename T4), \ + ESC_PARENS (T1, T2, T3, T4), \ + VALID, EXPR_TYPE, EXPR) + +#endif /* COMMON_VALID_EXPR_H */ diff --git a/gdb/gdbsupport/vec.c b/gdb/gdbsupport/vec.c new file mode 100644 index 00000000000..a8895449495 --- /dev/null +++ b/gdb/gdbsupport/vec.c @@ -0,0 +1,118 @@ +/* Vector API for GDB. + Copyright (C) 2004-2019 Free Software Foundation, Inc. + Contributed by Nathan Sidwell <nathan@codesourcery.com> + + 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 "vec.h" + +struct vec_prefix +{ + unsigned num; + unsigned alloc; + void *vec[1]; +}; + +/* Calculate the new ALLOC value, making sure that abs(RESERVE) slots + are free. If RESERVE < 0 grow exactly, otherwise grow + exponentially. */ + +static inline unsigned +calculate_allocation (const struct vec_prefix *pfx, int reserve) +{ + unsigned alloc = 0; + unsigned num = 0; + + if (pfx) + { + alloc = pfx->alloc; + num = pfx->num; + } + else if (!reserve) + /* If there's no prefix, and we've not requested anything, then we + will create a NULL vector. */ + return 0; + + /* We must have run out of room. */ + gdb_assert (alloc - num < (unsigned)(reserve < 0 ? -reserve : reserve)); + + if (reserve < 0) + /* Exact size. */ + alloc = num + -reserve; + else + { + /* Exponential growth. */ + if (!alloc) + alloc = 4; + else if (alloc < 16) + /* Double when small. */ + alloc = alloc * 2; + else + /* Grow slower when large. */ + alloc = (alloc * 3 / 2); + + /* If this is still too small, set it to the right size. */ + if (alloc < num + reserve) + alloc = num + reserve; + } + return alloc; +} + +/* Ensure there are at least abs(RESERVE) free slots in VEC. If + RESERVE < 0 grow exactly, else grow exponentially. As a special + case, if VEC is NULL, and RESERVE is 0, no vector will be created. */ + +void * +vec_p_reserve (void *vec, int reserve) +{ + return vec_o_reserve (vec, reserve, + offsetof (struct vec_prefix, vec), sizeof (void *)); +} + +/* As vec_p_reserve, but for object vectors. The vector's trailing + array is at VEC_OFFSET offset and consists of ELT_SIZE sized + elements. */ + +void * +vec_o_reserve (void *vec, int reserve, size_t vec_offset, size_t elt_size) +{ + struct vec_prefix *pfx = (struct vec_prefix *) vec; + unsigned alloc = calculate_allocation (pfx, reserve); + + if (!alloc) + return NULL; + + vec = xrealloc (vec, vec_offset + alloc * elt_size); + ((struct vec_prefix *)vec)->alloc = alloc; + if (!pfx) + ((struct vec_prefix *)vec)->num = 0; + + return vec; +} + +#if 0 +/* Example uses. */ +DEF_VEC_I (int); +typedef struct X +{ + int i; +} obj_t; +typedef obj_t *ptr_t; + +DEF_VEC_P (ptr_t); +DEF_VEC_O (obj_t); +#endif diff --git a/gdb/gdbsupport/vec.h b/gdb/gdbsupport/vec.h new file mode 100644 index 00000000000..8ac146d4903 --- /dev/null +++ b/gdb/gdbsupport/vec.h @@ -0,0 +1,1150 @@ +/* Vector API for GDB. + Copyright (C) 2004-2019 Free Software Foundation, Inc. + Contributed by Nathan Sidwell <nathan@codesourcery.com> + + 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/>. */ + +#ifndef COMMON_VEC_H +#define COMMON_VEC_H + +#include "diagnostics.h" + +/* clang has a bug that makes it warn (-Wunused-function) about unused functions + that are the result of the DEF_VEC_* macro expansion. See: + + https://bugs.llvm.org/show_bug.cgi?id=22712 + + We specifically ignore this warning for the vec functions when the compiler + is clang. */ +#ifdef __clang__ +# define DIAGNOSTIC_IGNORE_UNUSED_VEC_FUNCTION \ + DIAGNOSTIC_IGNORE_UNUSED_FUNCTION +#else +# define DIAGNOSTIC_IGNORE_UNUSED_VEC_FUNCTION +#endif + +/* The macros here implement a set of templated vector types and + associated interfaces. These templates are implemented with + macros, as we're not in C++ land. The interface functions are + typesafe and use static inline functions, sometimes backed by + out-of-line generic functions. + + Because of the different behavior of structure objects, scalar + objects and of pointers, there are three flavors, one for each of + these variants. Both the structure object and pointer variants + pass pointers to objects around -- in the former case the pointers + are stored into the vector and in the latter case the pointers are + dereferenced and the objects copied into the vector. The scalar + object variant is suitable for int-like objects, and the vector + elements are returned by value. + + There are both 'index' and 'iterate' accessors. The iterator + returns a boolean iteration condition and updates the iteration + variable passed by reference. Because the iterator will be + inlined, the address-of can be optimized away. + + The vectors are implemented using the trailing array idiom, thus + they are not resizeable without changing the address of the vector + object itself. This means you cannot have variables or fields of + vector type -- always use a pointer to a vector. The one exception + is the final field of a structure, which could be a vector type. + You will have to use the embedded_size & embedded_init calls to + create such objects, and they will probably not be resizeable (so + don't use the 'safe' allocation variants). The trailing array + idiom is used (rather than a pointer to an array of data), because, + if we allow NULL to also represent an empty vector, empty vectors + occupy minimal space in the structure containing them. + + Each operation that increases the number of active elements is + available in 'quick' and 'safe' variants. The former presumes that + there is sufficient allocated space for the operation to succeed + (it dies if there is not). The latter will reallocate the + vector, if needed. Reallocation causes an exponential increase in + vector size. If you know you will be adding N elements, it would + be more efficient to use the reserve operation before adding the + elements with the 'quick' operation. This will ensure there are at + least as many elements as you ask for, it will exponentially + increase if there are too few spare slots. If you want reserve a + specific number of slots, but do not want the exponential increase + (for instance, you know this is the last allocation), use a + negative number for reservation. You can also create a vector of a + specific size from the get go. + + You should prefer the push and pop operations, as they append and + remove from the end of the vector. If you need to remove several + items in one go, use the truncate operation. The insert and remove + operations allow you to change elements in the middle of the + vector. There are two remove operations, one which preserves the + element ordering 'ordered_remove', and one which does not + 'unordered_remove'. The latter function copies the end element + into the removed slot, rather than invoke a memmove operation. The + 'lower_bound' function will determine where to place an item in the + array using insert that will maintain sorted order. + + If you need to directly manipulate a vector, then the 'address' + accessor will return the address of the start of the vector. Also + the 'space' predicate will tell you whether there is spare capacity + in the vector. You will not normally need to use these two functions. + + Vector types are defined using a DEF_VEC_{O,P,I}(TYPEDEF) macro. + Variables of vector type are declared using a VEC(TYPEDEF) macro. + The characters O, P and I indicate whether TYPEDEF is a pointer + (P), object (O) or integral (I) type. Be careful to pick the + correct one, as you'll get an awkward and inefficient API if you + use the wrong one. There is a check, which results in a + compile-time warning, for the P and I versions, but there is no + check for the O versions, as that is not possible in plain C. + + An example of their use would be, + + DEF_VEC_P(tree); // non-managed tree vector. + + struct my_struct { + VEC(tree) *v; // A (pointer to) a vector of tree pointers. + }; + + struct my_struct *s; + + if (VEC_length(tree, s->v)) { we have some contents } + VEC_safe_push(tree, s->v, decl); // append some decl onto the end + for (ix = 0; VEC_iterate(tree, s->v, ix, elt); ix++) + { do something with elt } + +*/ + +/* Macros to invoke API calls. A single macro works for both pointer + and object vectors, but the argument and return types might well be + different. In each macro, T is the typedef of the vector elements. + Some of these macros pass the vector, V, by reference (by taking + its address), this is noted in the descriptions. */ + +/* Length of vector + unsigned VEC_T_length(const VEC(T) *v); + + Return the number of active elements in V. V can be NULL, in which + case zero is returned. */ + +#define VEC_length(T,V) (VEC_OP(T,length)(V)) + + +/* Check if vector is empty + int VEC_T_empty(const VEC(T) *v); + + Return nonzero if V is an empty vector (or V is NULL), zero otherwise. */ + +#define VEC_empty(T,V) (VEC_length (T,V) == 0) + + +/* Get the final element of the vector. + T VEC_T_last(VEC(T) *v); // Integer + T VEC_T_last(VEC(T) *v); // Pointer + T *VEC_T_last(VEC(T) *v); // Object + + Return the final element. V must not be empty. */ + +#define VEC_last(T,V) (VEC_OP(T,last)(V VEC_ASSERT_INFO)) + +/* Index into vector + T VEC_T_index(VEC(T) *v, unsigned ix); // Integer + T VEC_T_index(VEC(T) *v, unsigned ix); // Pointer + T *VEC_T_index(VEC(T) *v, unsigned ix); // Object + + Return the IX'th element. If IX must be in the domain of V. */ + +#define VEC_index(T,V,I) (VEC_OP(T,index)(V,I VEC_ASSERT_INFO)) + +/* Iterate over vector + int VEC_T_iterate(VEC(T) *v, unsigned ix, T &ptr); // Integer + int VEC_T_iterate(VEC(T) *v, unsigned ix, T &ptr); // Pointer + int VEC_T_iterate(VEC(T) *v, unsigned ix, T *&ptr); // Object + + Return iteration condition and update PTR to point to the IX'th + element. At the end of iteration, sets PTR to NULL. Use this to + iterate over the elements of a vector as follows, + + for (ix = 0; VEC_iterate(T,v,ix,ptr); ix++) + continue; */ + +#define VEC_iterate(T,V,I,P) (VEC_OP(T,iterate)(V,I,&(P))) + +/* Allocate new vector. + VEC(T,A) *VEC_T_alloc(int reserve); + + Allocate a new vector with space for RESERVE objects. If RESERVE + is zero, NO vector is created. */ + +#define VEC_alloc(T,N) (VEC_OP(T,alloc)(N)) + +/* Free a vector. + void VEC_T_free(VEC(T,A) *&); + + Free a vector and set it to NULL. */ + +#define VEC_free(T,V) (VEC_OP(T,free)(&V)) + +/* A cleanup function for a vector. + void VEC_T_cleanup(void *); + + Clean up a vector. */ + +#define VEC_cleanup(T) (VEC_OP(T,cleanup)) + +/* Use these to determine the required size and initialization of a + vector embedded within another structure (as the final member). + + size_t VEC_T_embedded_size(int reserve); + void VEC_T_embedded_init(VEC(T) *v, int reserve); + + These allow the caller to perform the memory allocation. */ + +#define VEC_embedded_size(T,N) (VEC_OP(T,embedded_size)(N)) +#define VEC_embedded_init(T,O,N) (VEC_OP(T,embedded_init)(VEC_BASE(O),N)) + +/* Copy a vector. + VEC(T,A) *VEC_T_copy(VEC(T) *); + + Copy the live elements of a vector into a new vector. The new and + old vectors need not be allocated by the same mechanism. */ + +#define VEC_copy(T,V) (VEC_OP(T,copy)(V)) + +/* Merge two vectors. + VEC(T,A) *VEC_T_merge(VEC(T) *, VEC(T) *); + + Copy the live elements of both vectors into a new vector. The new + and old vectors need not be allocated by the same mechanism. */ +#define VEC_merge(T,V1,V2) (VEC_OP(T,merge)(V1, V2)) + +/* Determine if a vector has additional capacity. + + int VEC_T_space (VEC(T) *v,int reserve) + + If V has space for RESERVE additional entries, return nonzero. You + usually only need to use this if you are doing your own vector + reallocation, for instance on an embedded vector. This returns + nonzero in exactly the same circumstances that VEC_T_reserve + will. */ + +#define VEC_space(T,V,R) (VEC_OP(T,space)(V,R VEC_ASSERT_INFO)) + +/* Reserve space. + int VEC_T_reserve(VEC(T,A) *&v, int reserve); + + Ensure that V has at least abs(RESERVE) slots available. The + signedness of RESERVE determines the reallocation behavior. A + negative value will not create additional headroom beyond that + requested. A positive value will create additional headroom. Note + this can cause V to be reallocated. Returns nonzero iff + reallocation actually occurred. */ + +#define VEC_reserve(T,V,R) (VEC_OP(T,reserve)(&(V),R VEC_ASSERT_INFO)) + +/* Push object with no reallocation + T *VEC_T_quick_push (VEC(T) *v, T obj); // Integer + T *VEC_T_quick_push (VEC(T) *v, T obj); // Pointer + T *VEC_T_quick_push (VEC(T) *v, T *obj); // Object + + Push a new element onto the end, returns a pointer to the slot + filled in. For object vectors, the new value can be NULL, in which + case NO initialization is performed. There must + be sufficient space in the vector. */ + +#define VEC_quick_push(T,V,O) (VEC_OP(T,quick_push)(V,O VEC_ASSERT_INFO)) + +/* Push object with reallocation + T *VEC_T_safe_push (VEC(T,A) *&v, T obj); // Integer + T *VEC_T_safe_push (VEC(T,A) *&v, T obj); // Pointer + T *VEC_T_safe_push (VEC(T,A) *&v, T *obj); // Object + + Push a new element onto the end, returns a pointer to the slot + filled in. For object vectors, the new value can be NULL, in which + case NO initialization is performed. Reallocates V, if needed. */ + +#define VEC_safe_push(T,V,O) (VEC_OP(T,safe_push)(&(V),O VEC_ASSERT_INFO)) + +/* Pop element off end + T VEC_T_pop (VEC(T) *v); // Integer + T VEC_T_pop (VEC(T) *v); // Pointer + void VEC_T_pop (VEC(T) *v); // Object + + Pop the last element off the end. Returns the element popped, for + pointer vectors. */ + +#define VEC_pop(T,V) (VEC_OP(T,pop)(V VEC_ASSERT_INFO)) + +/* Truncate to specific length + void VEC_T_truncate (VEC(T) *v, unsigned len); + + Set the length as specified. The new length must be less than or + equal to the current length. This is an O(1) operation. */ + +#define VEC_truncate(T,V,I) \ + (VEC_OP(T,truncate)(V,I VEC_ASSERT_INFO)) + +/* Grow to a specific length. + void VEC_T_safe_grow (VEC(T,A) *&v, int len); + + Grow the vector to a specific length. The LEN must be as + long or longer than the current length. The new elements are + uninitialized. */ + +#define VEC_safe_grow(T,V,I) \ + (VEC_OP(T,safe_grow)(&(V),I VEC_ASSERT_INFO)) + +/* Replace element + T VEC_T_replace (VEC(T) *v, unsigned ix, T val); // Integer + T VEC_T_replace (VEC(T) *v, unsigned ix, T val); // Pointer + T *VEC_T_replace (VEC(T) *v, unsigned ix, T *val); // Object + + Replace the IXth element of V with a new value, VAL. For pointer + vectors returns the original value. For object vectors returns a + pointer to the new value. For object vectors the new value can be + NULL, in which case no overwriting of the slot is actually + performed. */ + +#define VEC_replace(T,V,I,O) (VEC_OP(T,replace)(V,I,O VEC_ASSERT_INFO)) + +/* Insert object with no reallocation + T *VEC_T_quick_insert (VEC(T) *v, unsigned ix, T val); // Integer + T *VEC_T_quick_insert (VEC(T) *v, unsigned ix, T val); // Pointer + T *VEC_T_quick_insert (VEC(T) *v, unsigned ix, T *val); // Object + + Insert an element, VAL, at the IXth position of V. Return a pointer + to the slot created. For vectors of object, the new value can be + NULL, in which case no initialization of the inserted slot takes + place. There must be sufficient space. */ + +#define VEC_quick_insert(T,V,I,O) \ + (VEC_OP(T,quick_insert)(V,I,O VEC_ASSERT_INFO)) + +/* Insert object with reallocation + T *VEC_T_safe_insert (VEC(T,A) *&v, unsigned ix, T val); // Integer + T *VEC_T_safe_insert (VEC(T,A) *&v, unsigned ix, T val); // Pointer + T *VEC_T_safe_insert (VEC(T,A) *&v, unsigned ix, T *val); // Object + + Insert an element, VAL, at the IXth position of V. Return a pointer + to the slot created. For vectors of object, the new value can be + NULL, in which case no initialization of the inserted slot takes + place. Reallocate V, if necessary. */ + +#define VEC_safe_insert(T,V,I,O) \ + (VEC_OP(T,safe_insert)(&(V),I,O VEC_ASSERT_INFO)) + +/* Remove element retaining order + T VEC_T_ordered_remove (VEC(T) *v, unsigned ix); // Integer + T VEC_T_ordered_remove (VEC(T) *v, unsigned ix); // Pointer + void VEC_T_ordered_remove (VEC(T) *v, unsigned ix); // Object + + Remove an element from the IXth position of V. Ordering of + remaining elements is preserved. For pointer vectors returns the + removed object. This is an O(N) operation due to a memmove. */ + +#define VEC_ordered_remove(T,V,I) \ + (VEC_OP(T,ordered_remove)(V,I VEC_ASSERT_INFO)) + +/* Remove element destroying order + T VEC_T_unordered_remove (VEC(T) *v, unsigned ix); // Integer + T VEC_T_unordered_remove (VEC(T) *v, unsigned ix); // Pointer + void VEC_T_unordered_remove (VEC(T) *v, unsigned ix); // Object + + Remove an element from the IXth position of V. Ordering of + remaining elements is destroyed. For pointer vectors returns the + removed object. This is an O(1) operation. */ + +#define VEC_unordered_remove(T,V,I) \ + (VEC_OP(T,unordered_remove)(V,I VEC_ASSERT_INFO)) + +/* Remove a block of elements + void VEC_T_block_remove (VEC(T) *v, unsigned ix, unsigned len); + + Remove LEN elements starting at the IXth. Ordering is retained. + This is an O(N) operation due to memmove. */ + +#define VEC_block_remove(T,V,I,L) \ + (VEC_OP(T,block_remove)(V,I,L VEC_ASSERT_INFO)) + +/* Get the address of the array of elements + T *VEC_T_address (VEC(T) v) + + If you need to directly manipulate the array (for instance, you + want to feed it to qsort), use this accessor. */ + +#define VEC_address(T,V) (VEC_OP(T,address)(V)) + +/* Find the first index in the vector not less than the object. + unsigned VEC_T_lower_bound (VEC(T) *v, const T val, + int (*lessthan) (const T, const T)); // Integer + unsigned VEC_T_lower_bound (VEC(T) *v, const T val, + int (*lessthan) (const T, const T)); // Pointer + unsigned VEC_T_lower_bound (VEC(T) *v, const T *val, + int (*lessthan) (const T*, const T*)); // Object + + Find the first position in which VAL could be inserted without + changing the ordering of V. LESSTHAN is a function that returns + true if the first argument is strictly less than the second. */ + +#define VEC_lower_bound(T,V,O,LT) \ + (VEC_OP(T,lower_bound)(V,O,LT VEC_ASSERT_INFO)) + +/* Reallocate an array of elements with prefix. */ +extern void *vec_p_reserve (void *, int); +extern void *vec_o_reserve (void *, int, size_t, size_t); +#define vec_free_(V) xfree (V) + +#define VEC_ASSERT_INFO ,__FILE__,__LINE__ +#define VEC_ASSERT_DECL ,const char *file_,unsigned line_ +#define VEC_ASSERT_PASS ,file_,line_ +#define vec_assert(expr, op) \ + ((void)((expr) ? 0 : (gdb_assert_fail (op, file_, line_, \ + FUNCTION_NAME), 0))) + +#define VEC(T) VEC_##T +#define VEC_OP(T,OP) VEC_##T##_##OP + +#define VEC_T(T) \ +typedef struct VEC(T) \ +{ \ + unsigned num; \ + unsigned alloc; \ + T vec[1]; \ +} VEC(T) + +/* Vector of integer-like object. */ +#define DEF_VEC_I(T) \ +DIAGNOSTIC_PUSH \ +DIAGNOSTIC_IGNORE_UNUSED_VEC_FUNCTION \ +static inline void VEC_OP (T,must_be_integral_type) (void) \ +{ \ + (void)~(T)0; \ +} \ + \ +VEC_T(T); \ +DEF_VEC_FUNC_P(T) \ +DEF_VEC_ALLOC_FUNC_I(T) \ +DIAGNOSTIC_POP \ +struct vec_swallow_trailing_semi + +/* Vector of pointer to object. */ +#define DEF_VEC_P(T) \ +DIAGNOSTIC_PUSH \ +DIAGNOSTIC_IGNORE_UNUSED_VEC_FUNCTION \ +static inline void VEC_OP (T,must_be_pointer_type) (void) \ +{ \ + (void)((T)1 == (void *)1); \ +} \ + \ +VEC_T(T); \ +DEF_VEC_FUNC_P(T) \ +DEF_VEC_ALLOC_FUNC_P(T) \ +DIAGNOSTIC_POP \ +struct vec_swallow_trailing_semi + +/* Vector of object. */ +#define DEF_VEC_O(T) \ +DIAGNOSTIC_PUSH \ +DIAGNOSTIC_IGNORE_UNUSED_VEC_FUNCTION \ +VEC_T(T); \ +DEF_VEC_FUNC_O(T) \ +DEF_VEC_ALLOC_FUNC_O(T) \ +DIAGNOSTIC_POP \ +struct vec_swallow_trailing_semi + +/* Avoid offsetof (or its usual C implementation) as it triggers + -Winvalid-offsetof warnings with enum_flags types with G++ <= 4.4, + even though those types are memcpyable. This requires allocating a + dummy local VEC in all routines that use this, but that has the + advantage that it only works if T is default constructible, which + is exactly a check we want, to keep C compatibility. */ +#define vec_offset(T, VPTR) ((size_t) ((char *) &(VPTR)->vec - (char *) VPTR)) + +#define DEF_VEC_ALLOC_FUNC_I(T) \ +static inline VEC(T) *VEC_OP (T,alloc) \ + (int alloc_) \ +{ \ + VEC(T) dummy; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + return (VEC(T) *) vec_o_reserve (NULL, -alloc_, \ + vec_offset (T, &dummy), sizeof (T)); \ +} \ + \ +static inline VEC(T) *VEC_OP (T,copy) (VEC(T) *vec_) \ +{ \ + size_t len_ = vec_ ? vec_->num : 0; \ + VEC (T) *new_vec_ = NULL; \ + \ + if (len_) \ + { \ + VEC(T) dummy; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *) \ + vec_o_reserve (NULL, -len_, vec_offset (T, &dummy), sizeof (T)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec_->vec, sizeof (T) * len_); \ + } \ + return new_vec_; \ +} \ + \ +static inline VEC(T) *VEC_OP (T,merge) (VEC(T) *vec1_, VEC(T) *vec2_) \ +{ \ + if (vec1_ && vec2_) \ + { \ + VEC(T) dummy; \ + size_t len_ = vec1_->num + vec2_->num; \ + VEC (T) *new_vec_ = NULL; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *) \ + vec_o_reserve (NULL, -len_, vec_offset (T, &dummy), sizeof (T)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec1_->vec, sizeof (T) * vec1_->num); \ + memcpy (new_vec_->vec + vec1_->num, vec2_->vec, \ + sizeof (T) * vec2_->num); \ + \ + return new_vec_; \ + } \ + else \ + return VEC_copy (T, vec1_ ? vec1_ : vec2_); \ +} \ + \ +static inline void VEC_OP (T,free) \ + (VEC(T) **vec_) \ +{ \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline void VEC_OP (T,cleanup) \ + (void *arg_) \ +{ \ + VEC(T) **vec_ = (VEC(T) **) arg_; \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline int VEC_OP (T,reserve) \ + (VEC(T) **vec_, int alloc_ VEC_ASSERT_DECL) \ +{ \ + VEC(T) dummy; \ + int extend = !VEC_OP (T,space) \ + (*vec_, alloc_ < 0 ? -alloc_ : alloc_ VEC_ASSERT_PASS); \ + \ + if (extend) \ + *vec_ = (VEC(T) *) vec_o_reserve (*vec_, alloc_, \ + vec_offset (T, &dummy), sizeof (T)); \ + \ + return extend; \ +} \ + \ +static inline void VEC_OP (T,safe_grow) \ + (VEC(T) **vec_, int size_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (size_ >= 0 && VEC_OP(T,length) (*vec_) <= (unsigned)size_, \ + "safe_grow"); \ + VEC_OP (T,reserve) (vec_, (int)(*vec_ ? (*vec_)->num : 0) - size_ \ + VEC_ASSERT_PASS); \ + (*vec_)->num = size_; \ +} \ + \ +static inline T *VEC_OP (T,safe_push) \ + (VEC(T) **vec_, const T obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_push) (*vec_, obj_ VEC_ASSERT_PASS); \ +} \ + \ +static inline T *VEC_OP (T,safe_insert) \ + (VEC(T) **vec_, unsigned ix_, const T obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_insert) (*vec_, ix_, obj_ VEC_ASSERT_PASS); \ +} + +#define DEF_VEC_FUNC_P(T) \ +static inline unsigned VEC_OP (T,length) (const VEC(T) *vec_) \ +{ \ + return vec_ ? vec_->num : 0; \ +} \ + \ +static inline T VEC_OP (T,last) \ + (const VEC(T) *vec_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ && vec_->num, "last"); \ + \ + return vec_->vec[vec_->num - 1]; \ +} \ + \ +static inline T VEC_OP (T,index) \ + (const VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ && ix_ < vec_->num, "index"); \ + \ + return vec_->vec[ix_]; \ +} \ + \ +static inline int VEC_OP (T,iterate) \ + (const VEC(T) *vec_, unsigned ix_, T *ptr) \ +{ \ + if (vec_ && ix_ < vec_->num) \ + { \ + *ptr = vec_->vec[ix_]; \ + return 1; \ + } \ + else \ + { \ + *ptr = (T) 0; \ + return 0; \ + } \ +} \ + \ +static inline size_t VEC_OP (T,embedded_size) \ + (int alloc_) \ +{ \ + VEC(T) dummy; \ + \ + return vec_offset (T, &dummy) + alloc_ * sizeof(T); \ +} \ + \ +static inline void VEC_OP (T,embedded_init) \ + (VEC(T) *vec_, int alloc_) \ +{ \ + vec_->num = 0; \ + vec_->alloc = alloc_; \ +} \ + \ +static inline int VEC_OP (T,space) \ + (VEC(T) *vec_, int alloc_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (alloc_ >= 0, "space"); \ + return vec_ ? vec_->alloc - vec_->num >= (unsigned)alloc_ : !alloc_; \ +} \ + \ +static inline T *VEC_OP (T,quick_push) \ + (VEC(T) *vec_, T obj_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (vec_->num < vec_->alloc, "quick_push"); \ + slot_ = &vec_->vec[vec_->num++]; \ + *slot_ = obj_; \ + \ + return slot_; \ +} \ + \ +static inline T VEC_OP (T,pop) (VEC(T) *vec_ VEC_ASSERT_DECL) \ +{ \ + T obj_; \ + \ + vec_assert (vec_->num, "pop"); \ + obj_ = vec_->vec[--vec_->num]; \ + \ + return obj_; \ +} \ + \ +static inline void VEC_OP (T,truncate) \ + (VEC(T) *vec_, unsigned size_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ ? vec_->num >= size_ : !size_, "truncate"); \ + if (vec_) \ + vec_->num = size_; \ +} \ + \ +static inline T VEC_OP (T,replace) \ + (VEC(T) *vec_, unsigned ix_, T obj_ VEC_ASSERT_DECL) \ +{ \ + T old_obj_; \ + \ + vec_assert (ix_ < vec_->num, "replace"); \ + old_obj_ = vec_->vec[ix_]; \ + vec_->vec[ix_] = obj_; \ + \ + return old_obj_; \ +} \ + \ +static inline T *VEC_OP (T,quick_insert) \ + (VEC(T) *vec_, unsigned ix_, T obj_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (vec_->num < vec_->alloc && ix_ <= vec_->num, "quick_insert"); \ + slot_ = &vec_->vec[ix_]; \ + memmove (slot_ + 1, slot_, (vec_->num++ - ix_) * sizeof (T)); \ + *slot_ = obj_; \ + \ + return slot_; \ +} \ + \ +static inline T VEC_OP (T,ordered_remove) \ + (VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + T obj_; \ + \ + vec_assert (ix_ < vec_->num, "ordered_remove"); \ + slot_ = &vec_->vec[ix_]; \ + obj_ = *slot_; \ + memmove (slot_, slot_ + 1, (--vec_->num - ix_) * sizeof (T)); \ + \ + return obj_; \ +} \ + \ +static inline T VEC_OP (T,unordered_remove) \ + (VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + T obj_; \ + \ + vec_assert (ix_ < vec_->num, "unordered_remove"); \ + slot_ = &vec_->vec[ix_]; \ + obj_ = *slot_; \ + *slot_ = vec_->vec[--vec_->num]; \ + \ + return obj_; \ +} \ + \ +static inline void VEC_OP (T,block_remove) \ + (VEC(T) *vec_, unsigned ix_, unsigned len_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (ix_ + len_ <= vec_->num, "block_remove"); \ + slot_ = &vec_->vec[ix_]; \ + vec_->num -= len_; \ + memmove (slot_, slot_ + len_, (vec_->num - ix_) * sizeof (T)); \ +} \ + \ +static inline T *VEC_OP (T,address) \ + (VEC(T) *vec_) \ +{ \ + return vec_ ? vec_->vec : 0; \ +} \ + \ +static inline unsigned VEC_OP (T,lower_bound) \ + (VEC(T) *vec_, const T obj_, \ + int (*lessthan_)(const T, const T) VEC_ASSERT_DECL) \ +{ \ + unsigned int len_ = VEC_OP (T, length) (vec_); \ + unsigned int half_, middle_; \ + unsigned int first_ = 0; \ + while (len_ > 0) \ + { \ + T middle_elem_; \ + half_ = len_ >> 1; \ + middle_ = first_; \ + middle_ += half_; \ + middle_elem_ = VEC_OP (T,index) (vec_, middle_ VEC_ASSERT_PASS); \ + if (lessthan_ (middle_elem_, obj_)) \ + { \ + first_ = middle_; \ + ++first_; \ + len_ = len_ - half_ - 1; \ + } \ + else \ + len_ = half_; \ + } \ + return first_; \ +} + +#define DEF_VEC_ALLOC_FUNC_P(T) \ +static inline VEC(T) *VEC_OP (T,alloc) \ + (int alloc_) \ +{ \ + /* We must request exact size allocation, hence the negation. */ \ + return (VEC(T) *) vec_p_reserve (NULL, -alloc_); \ +} \ + \ +static inline void VEC_OP (T,free) \ + (VEC(T) **vec_) \ +{ \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline void VEC_OP (T,cleanup) \ + (void *arg_) \ +{ \ + VEC(T) **vec_ = (VEC(T) **) arg_; \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline VEC(T) *VEC_OP (T,copy) (VEC(T) *vec_) \ +{ \ + size_t len_ = vec_ ? vec_->num : 0; \ + VEC (T) *new_vec_ = NULL; \ + \ + if (len_) \ + { \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *)(vec_p_reserve (NULL, -len_)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec_->vec, sizeof (T) * len_); \ + } \ + return new_vec_; \ +} \ + \ +static inline VEC(T) *VEC_OP (T,merge) (VEC(T) *vec1_, VEC(T) *vec2_) \ +{ \ + if (vec1_ && vec2_) \ + { \ + size_t len_ = vec1_->num + vec2_->num; \ + VEC (T) *new_vec_ = NULL; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *)(vec_p_reserve (NULL, -len_)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec1_->vec, sizeof (T) * vec1_->num); \ + memcpy (new_vec_->vec + vec1_->num, vec2_->vec, \ + sizeof (T) * vec2_->num); \ + \ + return new_vec_; \ + } \ + else \ + return VEC_copy (T, vec1_ ? vec1_ : vec2_); \ +} \ + \ +static inline int VEC_OP (T,reserve) \ + (VEC(T) **vec_, int alloc_ VEC_ASSERT_DECL) \ +{ \ + int extend = !VEC_OP (T,space) \ + (*vec_, alloc_ < 0 ? -alloc_ : alloc_ VEC_ASSERT_PASS); \ + \ + if (extend) \ + *vec_ = (VEC(T) *) vec_p_reserve (*vec_, alloc_); \ + \ + return extend; \ +} \ + \ +static inline void VEC_OP (T,safe_grow) \ + (VEC(T) **vec_, int size_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (size_ >= 0 && VEC_OP(T,length) (*vec_) <= (unsigned)size_, \ + "safe_grow"); \ + VEC_OP (T,reserve) \ + (vec_, (int)(*vec_ ? (*vec_)->num : 0) - size_ VEC_ASSERT_PASS); \ + (*vec_)->num = size_; \ +} \ + \ +static inline T *VEC_OP (T,safe_push) \ + (VEC(T) **vec_, T obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_push) (*vec_, obj_ VEC_ASSERT_PASS); \ +} \ + \ +static inline T *VEC_OP (T,safe_insert) \ + (VEC(T) **vec_, unsigned ix_, T obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_insert) (*vec_, ix_, obj_ VEC_ASSERT_PASS); \ +} + +#define DEF_VEC_FUNC_O(T) \ +static inline unsigned VEC_OP (T,length) (const VEC(T) *vec_) \ +{ \ + return vec_ ? vec_->num : 0; \ +} \ + \ +static inline T *VEC_OP (T,last) (VEC(T) *vec_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ && vec_->num, "last"); \ + \ + return &vec_->vec[vec_->num - 1]; \ +} \ + \ +static inline T *VEC_OP (T,index) \ + (VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ && ix_ < vec_->num, "index"); \ + \ + return &vec_->vec[ix_]; \ +} \ + \ +static inline int VEC_OP (T,iterate) \ + (VEC(T) *vec_, unsigned ix_, T **ptr) \ +{ \ + if (vec_ && ix_ < vec_->num) \ + { \ + *ptr = &vec_->vec[ix_]; \ + return 1; \ + } \ + else \ + { \ + *ptr = 0; \ + return 0; \ + } \ +} \ + \ +static inline size_t VEC_OP (T,embedded_size) \ + (int alloc_) \ +{ \ + VEC(T) dummy; \ + \ + return vec_offset (T, &dummy) + alloc_ * sizeof(T); \ +} \ + \ +static inline void VEC_OP (T,embedded_init) \ + (VEC(T) *vec_, int alloc_) \ +{ \ + vec_->num = 0; \ + vec_->alloc = alloc_; \ +} \ + \ +static inline int VEC_OP (T,space) \ + (VEC(T) *vec_, int alloc_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (alloc_ >= 0, "space"); \ + return vec_ ? vec_->alloc - vec_->num >= (unsigned)alloc_ : !alloc_; \ +} \ + \ +static inline T *VEC_OP (T,quick_push) \ + (VEC(T) *vec_, const T *obj_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (vec_->num < vec_->alloc, "quick_push"); \ + slot_ = &vec_->vec[vec_->num++]; \ + if (obj_) \ + *slot_ = *obj_; \ + \ + return slot_; \ +} \ + \ +static inline void VEC_OP (T,pop) (VEC(T) *vec_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_->num, "pop"); \ + --vec_->num; \ +} \ + \ +static inline void VEC_OP (T,truncate) \ + (VEC(T) *vec_, unsigned size_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (vec_ ? vec_->num >= size_ : !size_, "truncate"); \ + if (vec_) \ + vec_->num = size_; \ +} \ + \ +static inline T *VEC_OP (T,replace) \ + (VEC(T) *vec_, unsigned ix_, const T *obj_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (ix_ < vec_->num, "replace"); \ + slot_ = &vec_->vec[ix_]; \ + if (obj_) \ + *slot_ = *obj_; \ + \ + return slot_; \ +} \ + \ +static inline T *VEC_OP (T,quick_insert) \ + (VEC(T) *vec_, unsigned ix_, const T *obj_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (vec_->num < vec_->alloc && ix_ <= vec_->num, "quick_insert"); \ + slot_ = &vec_->vec[ix_]; \ + memmove (slot_ + 1, slot_, (vec_->num++ - ix_) * sizeof (T)); \ + if (obj_) \ + *slot_ = *obj_; \ + \ + return slot_; \ +} \ + \ +static inline void VEC_OP (T,ordered_remove) \ + (VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (ix_ < vec_->num, "ordered_remove"); \ + slot_ = &vec_->vec[ix_]; \ + memmove (slot_, slot_ + 1, (--vec_->num - ix_) * sizeof (T)); \ +} \ + \ +static inline void VEC_OP (T,unordered_remove) \ + (VEC(T) *vec_, unsigned ix_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (ix_ < vec_->num, "unordered_remove"); \ + vec_->vec[ix_] = vec_->vec[--vec_->num]; \ +} \ + \ +static inline void VEC_OP (T,block_remove) \ + (VEC(T) *vec_, unsigned ix_, unsigned len_ VEC_ASSERT_DECL) \ +{ \ + T *slot_; \ + \ + vec_assert (ix_ + len_ <= vec_->num, "block_remove"); \ + slot_ = &vec_->vec[ix_]; \ + vec_->num -= len_; \ + memmove (slot_, slot_ + len_, (vec_->num - ix_) * sizeof (T)); \ +} \ + \ +static inline T *VEC_OP (T,address) \ + (VEC(T) *vec_) \ +{ \ + return vec_ ? vec_->vec : 0; \ +} \ + \ +static inline unsigned VEC_OP (T,lower_bound) \ + (VEC(T) *vec_, const T *obj_, \ + int (*lessthan_)(const T *, const T *) VEC_ASSERT_DECL) \ +{ \ + unsigned int len_ = VEC_OP (T, length) (vec_); \ + unsigned int half_, middle_; \ + unsigned int first_ = 0; \ + while (len_ > 0) \ + { \ + T *middle_elem_; \ + half_ = len_ >> 1; \ + middle_ = first_; \ + middle_ += half_; \ + middle_elem_ = VEC_OP (T,index) (vec_, middle_ VEC_ASSERT_PASS); \ + if (lessthan_ (middle_elem_, obj_)) \ + { \ + first_ = middle_; \ + ++first_; \ + len_ = len_ - half_ - 1; \ + } \ + else \ + len_ = half_; \ + } \ + return first_; \ +} + +#define DEF_VEC_ALLOC_FUNC_O(T) \ +static inline VEC(T) *VEC_OP (T,alloc) \ + (int alloc_) \ +{ \ + VEC(T) dummy; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + return (VEC(T) *) vec_o_reserve (NULL, -alloc_, \ + vec_offset (T, &dummy), sizeof (T)); \ +} \ + \ +static inline VEC(T) *VEC_OP (T,copy) (VEC(T) *vec_) \ +{ \ + size_t len_ = vec_ ? vec_->num : 0; \ + VEC (T) *new_vec_ = NULL; \ + \ + if (len_) \ + { \ + VEC(T) dummy; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *) \ + vec_o_reserve (NULL, -len_, vec_offset (T, &dummy), sizeof (T)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec_->vec, sizeof (T) * len_); \ + } \ + return new_vec_; \ +} \ + \ +static inline VEC(T) *VEC_OP (T,merge) (VEC(T) *vec1_, VEC(T) *vec2_) \ +{ \ + if (vec1_ && vec2_) \ + { \ + VEC(T) dummy; \ + size_t len_ = vec1_->num + vec2_->num; \ + VEC (T) *new_vec_ = NULL; \ + \ + /* We must request exact size allocation, hence the negation. */ \ + new_vec_ = (VEC (T) *) \ + vec_o_reserve (NULL, -len_, vec_offset (T, &dummy), sizeof (T)); \ + \ + new_vec_->num = len_; \ + memcpy (new_vec_->vec, vec1_->vec, sizeof (T) * vec1_->num); \ + memcpy (new_vec_->vec + vec1_->num, vec2_->vec, \ + sizeof (T) * vec2_->num); \ + \ + return new_vec_; \ + } \ + else \ + return VEC_copy (T, vec1_ ? vec1_ : vec2_); \ +} \ + \ +static inline void VEC_OP (T,free) \ + (VEC(T) **vec_) \ +{ \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline void VEC_OP (T,cleanup) \ + (void *arg_) \ +{ \ + VEC(T) **vec_ = (VEC(T) **) arg_; \ + if (*vec_) \ + vec_free_ (*vec_); \ + *vec_ = NULL; \ +} \ + \ +static inline int VEC_OP (T,reserve) \ + (VEC(T) **vec_, int alloc_ VEC_ASSERT_DECL) \ +{ \ + VEC(T) dummy; \ + int extend = !VEC_OP (T,space) (*vec_, alloc_ < 0 ? -alloc_ : alloc_ \ + VEC_ASSERT_PASS); \ + \ + if (extend) \ + *vec_ = (VEC(T) *) \ + vec_o_reserve (*vec_, alloc_, vec_offset (T, &dummy), sizeof (T)); \ + \ + return extend; \ +} \ + \ +static inline void VEC_OP (T,safe_grow) \ + (VEC(T) **vec_, int size_ VEC_ASSERT_DECL) \ +{ \ + vec_assert (size_ >= 0 && VEC_OP(T,length) (*vec_) <= (unsigned)size_, \ + "safe_grow"); \ + VEC_OP (T,reserve) \ + (vec_, (int)(*vec_ ? (*vec_)->num : 0) - size_ VEC_ASSERT_PASS); \ + (*vec_)->num = size_; \ +} \ + \ +static inline T *VEC_OP (T,safe_push) \ + (VEC(T) **vec_, const T *obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_push) (*vec_, obj_ VEC_ASSERT_PASS); \ +} \ + \ +static inline T *VEC_OP (T,safe_insert) \ + (VEC(T) **vec_, unsigned ix_, const T *obj_ VEC_ASSERT_DECL) \ +{ \ + VEC_OP (T,reserve) (vec_, 1 VEC_ASSERT_PASS); \ + \ + return VEC_OP (T,quick_insert) (*vec_, ix_, obj_ VEC_ASSERT_PASS); \ +} + +#endif /* COMMON_VEC_H */ diff --git a/gdb/gdbsupport/version.h b/gdb/gdbsupport/version.h new file mode 100644 index 00000000000..9e840e72b4d --- /dev/null +++ b/gdb/gdbsupport/version.h @@ -0,0 +1,31 @@ +/* Version information for GDB. + Copyright (C) 1999-2019 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/>. */ + +#ifndef COMMON_VERSION_H +#define COMMON_VERSION_H + +/* Version number of GDB, as a string. */ +extern const char version[]; + +/* Canonical host name as a string. */ +extern const char host_name[]; + +/* Canonical target name as a string. */ +extern const char target_name[]; + +#endif /* COMMON_VERSION_H */ diff --git a/gdb/gdbsupport/x86-xstate.h b/gdb/gdbsupport/x86-xstate.h new file mode 100644 index 00000000000..4ce8f61e394 --- /dev/null +++ b/gdb/gdbsupport/x86-xstate.h @@ -0,0 +1,88 @@ +/* Common code for x86 XSAVE extended state. + + Copyright (C) 2010-2019 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/>. */ + +#ifndef COMMON_X86_XSTATE_H +#define COMMON_X86_XSTATE_H + +/* The extended state feature bits. */ +#define X86_XSTATE_X87 (1ULL << 0) +#define X86_XSTATE_SSE (1ULL << 1) +#define X86_XSTATE_AVX (1ULL << 2) +#define X86_XSTATE_BNDREGS (1ULL << 3) +#define X86_XSTATE_BNDCFG (1ULL << 4) +#define X86_XSTATE_MPX (X86_XSTATE_BNDREGS | X86_XSTATE_BNDCFG) + +/* AVX 512 adds three feature bits. All three must be enabled. */ +#define X86_XSTATE_K (1ULL << 5) +#define X86_XSTATE_ZMM_H (1ULL << 6) +#define X86_XSTATE_ZMM (1ULL << 7) +#define X86_XSTATE_AVX512 (X86_XSTATE_K | X86_XSTATE_ZMM_H \ + | X86_XSTATE_ZMM) + +#define X86_XSTATE_PKRU (1ULL << 9) + +/* Supported mask and size of the extended state. */ +#define X86_XSTATE_X87_MASK X86_XSTATE_X87 +#define X86_XSTATE_SSE_MASK (X86_XSTATE_X87 | X86_XSTATE_SSE) +#define X86_XSTATE_AVX_MASK (X86_XSTATE_SSE_MASK | X86_XSTATE_AVX) +#define X86_XSTATE_MPX_MASK (X86_XSTATE_SSE_MASK | X86_XSTATE_MPX) +#define X86_XSTATE_AVX_MPX_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_MPX) +#define X86_XSTATE_AVX_AVX512_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_AVX512) +#define X86_XSTATE_AVX_MPX_AVX512_PKU_MASK (X86_XSTATE_AVX_MPX_MASK\ + | X86_XSTATE_AVX512 | X86_XSTATE_PKRU) + +#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_MPX_AVX512_PKU_MASK) + + +#define X86_XSTATE_SSE_SIZE 576 +#define X86_XSTATE_AVX_SIZE 832 +#define X86_XSTATE_BNDREGS_SIZE 1024 +#define X86_XSTATE_BNDCFG_SIZE 1088 +#define X86_XSTATE_AVX512_SIZE 2688 +#define X86_XSTATE_PKRU_SIZE 2696 +#define X86_XSTATE_MAX_SIZE 2696 + + +/* In case one of the MPX XCR0 bits is set we consider we have MPX. */ +#define HAS_MPX(XCR0) (((XCR0) & X86_XSTATE_MPX) != 0) +#define HAS_AVX(XCR0) (((XCR0) & X86_XSTATE_AVX) != 0) +#define HAS_AVX512(XCR0) (((XCR0) & X86_XSTATE_AVX512) != 0) +#define HAS_PKRU(XCR0) (((XCR0) & X86_XSTATE_PKRU) != 0) + +/* Get I386 XSAVE extended state size. */ +#define X86_XSTATE_SIZE(XCR0) \ + (HAS_PKRU (XCR0) ? X86_XSTATE_PKRU_SIZE : \ + (HAS_AVX512 (XCR0) ? X86_XSTATE_AVX512_SIZE : \ + (HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \ + (HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE)))) + +/* Initial value for fctrl register, as defined in the X86 manual, and + confirmed in the (Linux) kernel source. When the x87 floating point + feature is not enabled in an inferior we use this as the value of the + fcrtl register. */ + +#define I387_FCTRL_INIT_VAL 0x037f + +/* Initial value for mxcsr register. When the avx and sse floating point + features are not enabled in an inferior we use this as the value of the + mxcsr register. */ + +#define I387_MXCSR_INIT_VAL 0x1f80 + +#endif /* COMMON_X86_XSTATE_H */ diff --git a/gdb/gdbsupport/xml-utils.c b/gdb/gdbsupport/xml-utils.c new file mode 100644 index 00000000000..79f040e2b0f --- /dev/null +++ b/gdb/gdbsupport/xml-utils.c @@ -0,0 +1,63 @@ +/* Shared helper routines for manipulating XML. + + Copyright (C) 2006-2019 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 "xml-utils.h" + +/* See xml-utils.h. */ + +std::string +xml_escape_text (const char *text) +{ + std::string result; + + xml_escape_text_append (&result, text); + + return result; +} + +/* See xml-utils.h. */ + +void +xml_escape_text_append (std::string *result, const char *text) +{ + /* Expand the result. */ + for (int i = 0; text[i] != '\0'; i++) + switch (text[i]) + { + case '\'': + *result += "'"; + break; + case '\"': + *result += """; + break; + case '&': + *result += "&"; + break; + case '<': + *result += "<"; + break; + case '>': + *result += ">"; + break; + default: + *result += text[i]; + break; + } +} diff --git a/gdb/gdbsupport/xml-utils.h b/gdb/gdbsupport/xml-utils.h new file mode 100644 index 00000000000..747e8c8a481 --- /dev/null +++ b/gdb/gdbsupport/xml-utils.h @@ -0,0 +1,33 @@ +/* Shared helper routines for manipulating XML. + + Copyright (C) 2006-2019 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/>. */ + +#ifndef COMMON_XML_UTILS_H +#define COMMON_XML_UTILS_H + +/* Return a string with special characters from TEXT replaced by entity + references. */ + +extern std::string xml_escape_text (const char *text); + +/* Append TEXT to RESULT, with special characters replaced by entity + references. */ + +extern void xml_escape_text_append (std::string *result, const char *text); + +#endif /* COMMON_XML_UTILS_H */ |