+2017-08-31  Sergio Durigan Junior  <sergiodj@redhat.com>
+
+       * NEWS (Changes since GDB 8.0): Add entry mentioning new support
+       for setting/unsetting environment variables on the remote target.
+       (New remote packets): Add entries for QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset.
+       * common/environ.c (gdb_environ::operator=): Extend method to
+       handle m_user_set_env_list and m_user_unset_env_list.
+       (gdb_environ::clear): Likewise.
+       (match_var_in_string): Change type of first parameter from 'char
+       *' to 'const char *'.
+       (gdb_environ::set): Extend method to handle
+       m_user_set_env_list and m_user_unset_env_list.
+       (gdb_environ::unset): Likewise.
+       (gdb_environ::clear_user_set_env): New method.
+       (gdb_environ::user_set_envp): Likewise.
+       (gdb_environ::user_unset_envp): Likewise.
+       * common/environ.h (gdb_environ): Handle m_user_set_env_list and
+       m_user_unset_env_list on move constructor/assignment.
+       (unset): Add new default parameter 'update_unset_list = true'.
+       (clear_user_set_env): New method.
+       (user_set_envp): Likewise.
+       (user_unset_envp): Likewise.
+       (m_user_set_env_list): New std::set.
+       (m_user_unset_env_list): Likewise.
+       * common/rsp-low.c (hex2str): New function.
+       (bin2hex): New overload for bin2hex function.
+       * common/rsp-low.c (hex2str): New prototype.
+       (str2hex): New overload prototype.
+       * remote.c: Include "environ.h". Add QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset.
+       (remote_protocol_features): Add QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset packets.
+       (send_environment_packet): New function.
+       (extended_remote_environment_support): Likewise.
+       (extended_remote_create_inferior): Call
+       extended_remote_environment_support.
+       (_initialize_remote): Add QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset packet configs.
+       * unittests/environ-selftests.c (gdb_selftest_env_var):
+       New variable.
+       (test_vector_initialization): New function.
+       (test_init_from_host_environ): Likewise.
+       (test_reinit_from_host_environ): Likewise.
+       (test_set_A_unset_B_unset_A_cannot_find_A_can_find_B):
+       Likewise.
+       (test_unset_set_empty_vector): Likewise.
+       (test_vector_clear): Likewise.
+       (test_std_move): Likewise.
+       (test_move_constructor):
+       (test_self_move): Likewise.
+       (test_set_unset_reset): Likewise.
+       (run_tests): Rewrite in terms of the functions above.
+
 2017-08-31  Weimin Pan  <weimin.pan@oracle.com>
 
        * sparc64-tdep.c (adi_stat_t): Fix comment formatting.
 
 
 *** Changes since GDB 8.0
 
+* On Unix systems, GDB now supports transmitting environment variables
+  that are to be set or unset to GDBserver.  These variables will
+  affect the environment to be passed to the remote inferior.
+
+  To inform GDB of environment variables that are to be transmitted to
+  GDBserver, use the "set environment" command.  Only user set
+  environment variables are sent to GDBserver.
+
+  To inform GDB of environment variables that are to be unset before
+  the remote inferior is started by the GDBserver, use the "unset
+  environment" command.
+
 * New features in the GDB remote stub, GDBserver
 
   ** New "--selftest" command line option runs some GDBserver self
      "target remote", you can disable the startup with shell by using the
      new "--no-startup-with-shell" GDBserver command line option.
 
+  ** On Unix systems, GDBserver now supports receiving environment
+     variables that are to be set or unset from GDB.  These variables
+     will affect the environment to be passed to the inferior.
+
 * New remote packets
 
+QEnvironmentHexEncoded
+  Inform GDBserver of an environment variable that is to be passed to
+  the inferior when starting it.
+
+QEnvironmentUnset
+  Inform GDBserver of an environment variable that is to be unset
+  before starting the remote inferior.
+
+QEnvironmentReset
+  Inform GDBserver that the environment should be reset (i.e.,
+  user-set environment variables should be unset).
+
 QStartupWithShell
   Indicates whether the inferior must be started with a shell or not.
 
 
     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;
 }
 
   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
    if it contains, false otherwise.  */
 
 static bool
-match_var_in_string (char *string, const char *var, size_t var_len)
+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;
 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);
+  unset (var, false);
 
   /* Insert the element before the last one, which is always NULL.  */
-  m_environ_vector.insert (m_environ_vector.end () - 1,
-                          concat (var, "=", value, 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 common/environ.h.  */
 
 void
-gdb_environ::unset (const char *var)
+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 (std::vector<char *>::iterator el = m_environ_vector.begin ();
-       el != m_environ_vector.end () - 1;
-       ++el)
-    if (match_var_in_string (*el, var, len))
-      {
-       xfree (*el);
-       m_environ_vector.erase (el);
-       break;
-      }
+  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 common/environ.h.  */
+
+void
+gdb_environ::unset (const char *var)
+{
+  unset (var, true);
 }
 
 /* See common/environ.h.  */
 {
   return const_cast<char **> (&m_environ_vector[0]);
 }
+
+/* See common/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;
+}
 
 #define ENVIRON_H 1
 
 #include <vector>
+#include <set>
 
 /* Class that represents the environment variables as seen by the
    inferior.  */
 
   /* Move constructor.  */
   gdb_environ (gdb_environ &&e)
-    : m_environ_vector (std::move (e.m_environ_vector))
+    : 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.  */
   /* 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 /* defined (ENVIRON_H) */
 
 
 /* See rsp-low.h.  */
 
+std::string
+hex2str (const char *hex)
+{
+  std::string ret;
+  size_t len = strlen (hex);
+
+  ret.reserve (len / 2);
+  for (size_t i = 0; i < len; ++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)
 {
   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
 
 
 extern int hex2bin (const char *hex, gdb_byte *bin, int count);
 
+/* Like hex2bin, but return a std::string.  */
+
+extern std::string hex2str (const char *hex);
+
 /* 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
 
 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
 
+2017-08-31  Sergio Durigan Junior  <sergiodj@redhat.com>
+
+       * gdb.texinfo (set environment): Add @anchor.  Explain that
+       environment variables set by the user are sent to GDBserver.
+       (unset environment): Likewise, but for unsetting variables.
+       (Connecting) <Remote Packet>: Add "environment-hex-encoded",
+       "QEnvironmentHexEncoded", "environment-unset", "QEnvironmentUnset",
+       "environment-reset" and "QEnvironmentReset" to the table.
+       (Remote Protocol) <QEnvironmentHexEncoded, QEnvironmentUnset,
+       QEnvironmentReset>: New item, explaining the packet.
+
 2017-08-23  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.texinfo (Compiling and Injecting Code): Add to subsection
 
 your program.  You can abbreviate @code{environment} as @code{env}.
 
 @kindex set environment
+@anchor{set environment}
 @item set environment @var{varname} @r{[}=@var{value}@r{]}
 Set environment variable @var{varname} to @var{value}.  The value
 changes for your program (and the shell @value{GDBN} uses to launch
 wrapper instead of using @code{set environment}.  @xref{set
 exec-wrapper}, for an example doing just that.
 
+Environment variables that are set by the user are also transmitted to
+@command{gdbserver} to be used when starting the remote inferior.
+@pxref{QEnvironmentHexEncoded}.
+
 @kindex unset environment
+@anchor{unset environment}
 @item unset environment @var{varname}
 Remove variable @var{varname} from the environment to be passed to your
 program.  This is different from @samp{set env @var{varname} =};
 @code{unset environment} removes the variable from the environment,
 rather than assigning it an empty value.
+
+Environment variables that are unset by the user are also unset on
+@command{gdbserver} when starting the remote inferior.
+@pxref{QEnvironmentUnset}.
 @end table
 
 @emph{Warning:} On Unix systems, @value{GDBN} runs your program using
 @tab @code{QStartupWithShell}
 @tab @code{set startup-with-shell}
 
+@item @code{environment-hex-encoded}
+@tab @code{QEnvironmentHexEncoded}
+@tab @code{set environment}
+
+@item @code{environment-unset}
+@tab @code{QEnvironmentUnset}
+@tab @code{unset environment}
+
+@item @code{environment-reset}
+@tab @code{QEnvironmentReset}
+@tab @code{Reset the inferior environment (i.e., unset user-set variables)}
+
 @item @code{conditional-breakpoints-packet}
 @tab @code{Z0 and Z1}
 @tab @code{Support for target-side breakpoint condition evaluation}
 Use of this packet is controlled by the @code{set startup-with-shell}
 command; @pxref{set startup-with-shell}.
 
+@item QEnvironmentHexEncoded:@var{hex-value}
+@anchor{QEnvironmentHexEncoded}
+@cindex set environment variable, remote request
+@cindex @samp{QEnvironmentHexEncoded} packet
+On UNIX-like targets, it is possible to set environment variables that
+will be passed to the inferior during the startup process.  This
+packet is used to inform @command{gdbserver} of an environment
+variable that has been defined by the user on @value{GDBN} (@pxref{set
+environment}).
+
+The packet is composed by @var{hex-value}, an hex encoded
+representation of the @var{name=value} format representing an
+environment variable.  The name of the environment variable is
+represented by @var{name}, and the value to be assigned to the
+environment variable is represented by @var{value}.  If the variable
+has no value (i.e., the value is @code{null}), then @var{value} will
+not be present.
+
+This packet is only available in extended mode (@pxref{extended
+mode}).
+
+Reply:
+@table @samp
+@item OK
+The request succeeded.
+@end table
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response
+(@pxref{qSupported}).  This should only be done on targets that
+actually support passing environment variables to the starting
+inferior.
+
+This packet is related to the @code{set environment} command;
+@pxref{set environment}.
+
+@item QEnvironmentUnset:@var{hex-value}
+@anchor{QEnvironmentUnset}
+@cindex unset environment variable, remote request
+@cindex @samp{QEnvironmentUnset} packet
+On UNIX-like targets, it is possible to unset environment variables
+before starting the inferior in the remote target.  This packet is
+used to inform @command{gdbserver} of an environment variable that has
+been unset by the user on @value{GDBN} (@pxref{unset environment}).
+
+The packet is composed by @var{hex-value}, an hex encoded
+representation of the name of the environment variable to be unset.
+
+This packet is only available in extended mode (@pxref{extended
+mode}).
+
+Reply:
+@table @samp
+@item OK
+The request succeeded.
+@end table
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response
+(@pxref{qSupported}).  This should only be done on targets that
+actually support passing environment variables to the starting
+inferior.
+
+This packet is related to the @code{unset environment} command;
+@pxref{unset environment}.
+
+@item QEnvironmentReset
+@anchor{QEnvironmentReset}
+@cindex reset environment, remote request
+@cindex @samp{QEnvironmentReset} packet
+On UNIX-like targets, this packet is used to reset the state of
+environment variables in the remote target before starting the
+inferior.  In this context, reset means unsetting all environment
+variables that were previously set by the user (i.e., were not
+initially present in the environment).  It is sent to
+@command{gdbserver} before the @samp{QEnvironmentHexEncoded}
+(@pxref{QEnvironmentHexEncoded}) and the @samp{QEnvironmentUnset}
+(@pxref{QEnvironmentUnset}) packets.
+
+This packet is only available in extended mode (@pxref{extended
+mode}).
+
+Reply:
+@table @samp
+@item OK
+The request succeeded.
+@end table
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response
+(@pxref{qSupported}).  This should only be done on targets that
+actually support passing environment variables to the starting
+inferior.
+
 @item qfThreadInfo
 @itemx qsThreadInfo
 @cindex list active threads, remote request
 
+2017-08-31  Sergio Durigan Junior  <sergiodj@redhat.com>
+
+       * server.c (handle_general_set): Handle QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset packets.
+       (handle_query): Inform remote that QEnvironmentHexEncoded,
+       QEnvironmentUnset and QEnvironmentReset are supported.
+
 2017-08-25  Simon Marchi  <simon.marchi@ericsson.com>
 
        * inferiors.h (inferior_target_data): Rename to ...
 
       return;
     }
 
+  if (strcmp (own_buf, "QEnvironmentReset") == 0)
+    {
+      our_environ = gdb_environ::from_host_environ ();
+
+      write_ok (own_buf);
+      return;
+    }
+
+  if (startswith (own_buf, "QEnvironmentHexEncoded:"))
+    {
+      const char *p = own_buf + sizeof ("QEnvironmentHexEncoded:") - 1;
+      /* The final form of the environment variable.  FINAL_VAR will
+        hold the 'VAR=VALUE' format.  */
+      std::string final_var = hex2str (p);
+      std::string var_name, var_value;
+
+      if (remote_debug)
+       {
+         debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
+         debug_printf (_("[Environment variable to be set: '%s']\n"),
+                       final_var.c_str ());
+         debug_flush ();
+       }
+
+      size_t pos = final_var.find ('=');
+      if (pos == std::string::npos)
+       {
+         warning (_("Unexpected format for environment variable: '%s'"),
+                  final_var.c_str ());
+         write_enn (own_buf);
+         return;
+       }
+
+      var_name = final_var.substr (0, pos);
+      var_value = final_var.substr (pos + 1, std::string::npos);
+
+      our_environ.set (var_name.c_str (), var_value.c_str ());
+
+      write_ok (own_buf);
+      return;
+    }
+
+  if (startswith (own_buf, "QEnvironmentUnset:"))
+    {
+      const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
+      std::string varname = hex2str (p);
+
+      if (remote_debug)
+       {
+         debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
+         debug_printf (_("[Environment variable to be unset: '%s']\n"),
+                       varname.c_str ());
+         debug_flush ();
+       }
+
+      our_environ.unset (varname.c_str ());
+
+      write_ok (own_buf);
+      return;
+    }
+
   if (strcmp (own_buf, "QStartNoAckMode") == 0)
     {
       if (remote_debug)
        }
 
       sprintf (own_buf,
-              "PacketSize=%x;QPassSignals+;QProgramSignals+;QStartupWithShell+",
+              "PacketSize=%x;QPassSignals+;QProgramSignals+;"
+              "QStartupWithShell+;QEnvironmentHexEncoded+;"
+              "QEnvironmentReset+;QEnvironmentUnset+",
               PBUFSIZ - 1);
 
       if (target_supports_catch_syscall ())
 
 #include "record-btrace.h"
 #include <algorithm>
 #include "common/scoped_restore.h"
+#include "environ.h"
 
 /* Temp hacks for tracepoint encoding migration.  */
 static char *target_buf;
   PACKET_QCatchSyscalls,
   PACKET_QProgramSignals,
   PACKET_QStartupWithShell,
+  PACKET_QEnvironmentHexEncoded,
+  PACKET_QEnvironmentReset,
+  PACKET_QEnvironmentUnset,
   PACKET_qCRC,
   PACKET_qSearch_memory,
   PACKET_vAttach,
     PACKET_QProgramSignals },
   { "QStartupWithShell", PACKET_DISABLE, remote_supported_packet,
     PACKET_QStartupWithShell },
+  { "QEnvironmentHexEncoded", PACKET_DISABLE, remote_supported_packet,
+    PACKET_QEnvironmentHexEncoded },
+  { "QEnvironmentReset", PACKET_DISABLE, remote_supported_packet,
+    PACKET_QEnvironmentReset },
+  { "QEnvironmentUnset", PACKET_DISABLE, remote_supported_packet,
+    PACKET_QEnvironmentUnset },
   { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
     PACKET_QStartNoAckMode },
   { "multiprocess", PACKET_DISABLE, remote_supported_packet,
     }
 }
 
+/* Helper function to send set/unset environment packets.  ACTION is
+   either "set" or "unset".  PACKET is either "QEnvironmentHexEncoded"
+   or "QEnvironmentUnsetVariable".  VALUE is the variable to be
+   sent.  */
+
+static void
+send_environment_packet (struct remote_state *rs,
+                        const char *action,
+                        const char *packet,
+                        const char *value)
+{
+  /* Convert the environment variable to an hex string, which
+     is the best format to be transmitted over the wire.  */
+  std::string encoded_value = bin2hex ((const gdb_byte *) value,
+                                        strlen (value));
+
+  xsnprintf (rs->buf, get_remote_packet_size (),
+            "%s:%s", packet, encoded_value.c_str ());
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, &rs->buf_size, 0);
+  if (strcmp (rs->buf, "OK") != 0)
+    warning (_("Unable to %s environment variable '%s' on remote."),
+            action, value);
+}
+
+/* Helper function to handle the QEnvironment* packets.  */
+
+static void
+extended_remote_environment_support (struct remote_state *rs)
+{
+  if (packet_support (PACKET_QEnvironmentReset) != PACKET_DISABLE)
+    {
+      putpkt ("QEnvironmentReset");
+      getpkt (&rs->buf, &rs->buf_size, 0);
+      if (strcmp (rs->buf, "OK") != 0)
+       warning (_("Unable to reset environment on remote."));
+    }
+
+  gdb_environ *e = ¤t_inferior ()->environment;
+
+  if (packet_support (PACKET_QEnvironmentHexEncoded) != PACKET_DISABLE)
+    for (const std::string &el : e->user_set_env ())
+      send_environment_packet (rs, "set", "QEnvironmentHexEncoded",
+                              el.c_str ());
+
+  if (packet_support (PACKET_QEnvironmentUnset) != PACKET_DISABLE)
+    for (const std::string &el : e->user_unset_env ())
+      send_environment_packet (rs, "unset", "QEnvironmentUnset", el.c_str ());
+}
+
 /* In the extended protocol we want to be able to do things like
    "run" and have them basically work as expected.  So we need
    a special create_inferior function.  We support changing the
               rs->buf);
     }
 
+  extended_remote_environment_support (rs);
+
   /* Now restart the remote server.  */
   run_worked = extended_remote_run (args) != -1;
   if (!run_worked)
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QStartupWithShell],
                         "QStartupWithShell", "startup-with-shell", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets
+                        [PACKET_QEnvironmentHexEncoded],
+                        "QEnvironmentHexEncoded", "environment-hex-encoded",
+                        0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_QEnvironmentReset],
+                        "QEnvironmentReset", "environment-reset",
+                        0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_QEnvironmentUnset],
+                        "QEnvironmentUnset", "environment-unset",
+                        0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qSymbol],
                         "qSymbol", "symbol-lookup", 0);
 
 
+2017-08-31  Sergio Durigan Junior  <sergiodj@redhat.com>
+
+       * gdb.base/share-env-with-gdbserver.c: New file.
+       * gdb.base/share-env-with-gdbserver.exp: Likewise.
+
 2017-08-28  Simon Marchi  <simon.marchi@ericsson.com>
 
        * gdb.base/commands.exp (gdbvar_simple_if_test,
 
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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 <stdio.h>
+#include <stdlib.h>
+
+/* Wrapper around getenv for GDB.  */
+
+static const char *
+my_getenv (const char *name)
+{
+  return getenv (name);
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *myvar = getenv ("GDB_TEST_VAR");
+
+  if (myvar != NULL)
+    printf ("It worked!  myvar = '%s'\n", myvar);
+  else
+    printf ("It failed.");
+
+  return 0;    /* break-here */
+}
 
--- /dev/null
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 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/>.
+
+# This test doesn't make sense on native-gdbserver.
+if { [use_gdb_stub] } {
+    untested "not supported"
+    return
+}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+set test_var_name "GDB_TEST_VAR"
+
+# Helper function that performs a check on the output of "getenv".
+#
+# - VAR_NAME is the name of the variable to be checked.
+#
+# - VAR_VALUE is the value expected.
+#
+# - TEST_MSG, if not empty, is the test message to be used by the
+#   "gdb_test".
+#
+# - EMPTY_VAR_P, if non-zero, means that the variable is not expected
+#   to exist.  In this case, VAR_VALUE is not considered.
+
+proc check_getenv { var_name var_value { test_msg "" } { empty_var_p 0 } } {
+    global hex decimal
+
+    if { $test_msg == "" } {
+       set test_msg "print result of getenv for $var_name"
+    }
+
+    if { $empty_var_p } {
+       set var_value_match "0x0"
+    } else {
+       set var_value_match "$hex \"$var_value\""
+    }
+
+    gdb_test "print my_getenv (\"$var_name\")" "\\\$$decimal = $var_value_match" \
+       $test_msg
+}
+
+# Helper function to re-run to main and breaking at the "break-here"
+# label.
+
+proc do_prepare_inferior { } {
+    global decimal hex
+
+    if { ![runto_main] } {
+       return -1
+    }
+
+    gdb_breakpoint [gdb_get_line_number "break-here"]
+
+    gdb_test "continue" "Breakpoint $decimal, main \\\(argc=1, argv=$hex\\\) at.*" \
+       "continue until breakpoint"
+}
+
+# Helper function that does the actual testing.
+#
+# - VAR_VALUE is the value of the environment variable.
+#
+# - VAR_NAME is the name of the environment variable.  If empty,
+#   defaults to $test_var_name.
+#
+# - VAR_NAME_MATCH is the name (regex) that will be used to query the
+#   environment about the variable (via getenv).  This is useful when
+#   we're testing variables with strange names (e.g., with an equal
+#   sign in the name) and we know that the variable will actually be
+#   set using another name.  If empty, defatults, to $var_name.
+#
+# - VAR_VALUE_MATCH is the value (regex) that will be used to match
+#   the result of getenv.  The rationale is the same as explained for
+#   VAR_NAME_MATCH.  If empty, defaults, to $var_value.
+
+proc do_test { var_value { var_name "" } { var_name_match "" } { var_value_match "" } } {
+    global binfile test_var_name
+
+    clean_restart $binfile
+
+    if { $var_name == "" } {
+       set var_name $test_var_name
+    }
+
+    if { $var_name_match == "" } {
+       set var_name_match $var_name
+    }
+
+    if { $var_value_match == "" } {
+       set var_value_match $var_value
+    }
+
+    if { $var_value != "" } {
+       gdb_test_no_output "set environment $var_name = $var_value" \
+           "set $var_name = $var_value"
+    } else {
+       gdb_test "set environment $var_name =" \
+           "Setting environment variable \"$var_name\" to null value." \
+           "set $var_name to null value"
+    }
+
+    do_prepare_inferior
+
+    check_getenv "$var_name_match" "$var_value_match" \
+       "print result of getenv for $var_name"
+}
+
+with_test_prefix "long var value" {
+    do_test "this is my test variable; testing long vars; {}"
+}
+
+with_test_prefix "empty var" {
+    do_test ""
+}
+
+with_test_prefix "strange named var" {
+    # In this test we're doing the following:
+    #
+    #   (gdb) set environment 'asd =' = 123 43; asd b ### [];;;
+    #
+    # However, due to how GDB parses this line, the environment
+    # variable will end up named <'asd> (without the <>), and its
+    # value will be <' = 123 43; asd b ### [];;;> (without the <>).
+    do_test "123 43; asd b ### \[\];;;" "'asd ='" "'asd" \
+       [string_to_regexp "' = 123 43; asd b ### \[\];;;"]
+}
+
+# Test setting and unsetting environment variables in various
+# fashions.
+
+proc test_set_unset_vars { } {
+    global binfile
+
+    clean_restart $binfile
+
+    with_test_prefix "set 3 environment variables" {
+       # Set some environment variables
+       gdb_test_no_output "set environment A = 1" \
+           "set A to 1"
+       gdb_test_no_output "set environment B = 2" \
+           "set B to 2"
+       gdb_test_no_output "set environment C = 3" \
+           "set C to 3"
+
+       do_prepare_inferior
+
+       # Check that the variables are known by the inferior
+       check_getenv "A" "1"
+       check_getenv "B" "2"
+       check_getenv "C" "3"
+    }
+
+    with_test_prefix "unset one variable, reset one" {
+       # Now, unset/reset some values
+       gdb_test_no_output "unset environment A" \
+           "unset A"
+       gdb_test_no_output "set environment B = 4" \
+           "set B to 4"
+
+       do_prepare_inferior
+
+       check_getenv "A" "" "" 1
+       check_getenv "B" "4"
+       check_getenv "C" "3"
+    }
+
+    with_test_prefix "unset two variables, reset one" {
+       # Unset more values
+       gdb_test_no_output "unset environment B" \
+           "unset B"
+       gdb_test_no_output "set environment A = 1" \
+           "set A to 1 again"
+       gdb_test_no_output "unset environment C" \
+           "unset C"
+
+       do_prepare_inferior
+
+       check_getenv "A" "1"
+       check_getenv "B" "" "" 1
+       check_getenv "C" "" "" 1
+    }
+}
+
+with_test_prefix "test set/unset of vars" {
+    test_set_unset_vars
+}
+
+# Test that unsetting works.
+
+proc test_unset { } {
+    global hex decimal binfile gdb_prompt
+
+    clean_restart $binfile
+
+    do_prepare_inferior
+
+    set test_msg "check if unset works"
+    set found_home 0
+    gdb_test_multiple "print my_getenv (\"HOME\")" $test_msg {
+       -re "\\\$$decimal = $hex \".*\"\r\n$gdb_prompt $" {
+           pass $test_msg
+           set found_home 1
+       }
+       -re "\\\$$decimal = 0x0\r\n$gdb_prompt $" {
+           untested $test_msg
+       }
+    }
+
+    if { $found_home == 1 } {
+       with_test_prefix "simple unset" {
+           # We can do the test, because $HOME exists (and therefore can
+           # be unset).
+           gdb_test_no_output "unset environment HOME" "unset HOME"
+
+           do_prepare_inferior
+
+           # $HOME now must be empty
+           check_getenv "HOME" "" "" 1
+       }
+
+       with_test_prefix "set-then-unset" {
+           clean_restart $binfile
+
+           # Test if setting and then unsetting $HOME works.
+           gdb_test_no_output "set environment HOME = test" "set HOME as test"
+           gdb_test_no_output "unset environment HOME" "unset HOME again"
+
+           do_prepare_inferior
+
+           check_getenv "HOME" "" "" 1
+       }
+    }
+}
+
+with_test_prefix "test unset of vars" {
+    test_unset
+}
 
 #include "common/environ.h"
 #include "common/diagnostics.h"
 
+static const char gdb_selftest_env_var[] = "GDB_SELFTEST_ENVIRON";
+
+static bool
+set_contains (const std::set<std::string> &set, std::string key)
+{
+  return set.find (key) != set.end ();
+}
+
 namespace selftests {
 namespace gdb_environ_tests {
 
+/* Test if the vector is initialized in a correct way.  */
+
 static void
-run_tests ()
+test_vector_initialization ()
 {
-  /* Set a test environment variable.  This will be unset at the end
-     of this function.  */
-  if (setenv ("GDB_SELFTEST_ENVIRON", "1", 1) != 0)
-    error (_("Could not set environment variable for testing."));
-
   gdb_environ env;
 
   /* When the vector is initialized, there should always be one NULL
      element in it.  */
   SELF_CHECK (env.envp ()[0] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (env.user_unset_env ().size () == 0);
 
   /* Make sure that there is no other element.  */
   SELF_CHECK (env.get ("PWD") == NULL);
+}
 
-  /* Check if unset followed by a set in an empty vector works.  */
-  env.set ("PWD", "test");
-  SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0);
-  /* The second element must be NULL.  */
-  SELF_CHECK (env.envp ()[1] == NULL);
-  env.unset ("PWD");
-  SELF_CHECK (env.envp ()[0] == NULL);
+/* Test initialization using the host's environ.  */
 
+static void
+test_init_from_host_environ ()
+{
   /* Initialize the environment vector using the host's environ.  */
-  env = gdb_environ::from_host_environ ();
+  gdb_environ env = gdb_environ::from_host_environ ();
+
+  /* The user-set and user-unset lists must be empty.  */
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (env.user_unset_env ().size () == 0);
 
   /* Our test environment variable should be present at the
      vector.  */
-  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
-
-  /* Set our test variable to another value.  */
-  env.set ("GDB_SELFTEST_ENVIRON", "test");
-  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "test") == 0);
-
-  /* And unset our test variable.  The variable still exists in the
-     host's environment, but doesn't exist in our vector.  */
-  env.unset ("GDB_SELFTEST_ENVIRON");
-  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
-
-  /* Re-set the test variable.  */
-  env.set ("GDB_SELFTEST_ENVIRON", "1");
-  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
+  SELF_CHECK (strcmp (env.get (gdb_selftest_env_var), "1") == 0);
+}
 
-  /* When we clear our environ vector, there should be only one
-     element on it (NULL), and we shouldn't be able to get our test
-     variable.  */
-  env.clear ();
-  SELF_CHECK (env.envp ()[0] == NULL);
-  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
+/* Test reinitialization using the host's environ.  */
 
+static void
+test_reinit_from_host_environ ()
+{
   /* Reinitialize our environ vector using the host environ.  We
      should be able to see one (and only one) instance of the test
      variable.  */
+  gdb_environ env = gdb_environ::from_host_environ ();
   env = gdb_environ::from_host_environ ();
   char **envp = env.envp ();
   int num_found = 0;
     if (strcmp (envp[i], "GDB_SELFTEST_ENVIRON=1") == 0)
       ++num_found;
   SELF_CHECK (num_found == 1);
+}
 
-  /* Get rid of our test variable.  */
-  unsetenv ("GDB_SELFTEST_ENVIRON");
+/* Test the case when we set a variable A, then set a variable B,
+   then unset A, and make sure that we cannot find A in the environ
+   vector, but can still find B.  */
+
+static void
+test_set_A_unset_B_unset_A_cannot_find_A_can_find_B ()
+{
+  gdb_environ env;
 
-  /* Test the case when we set a variable A, then set a variable B,
-     then unset A, and make sure that we cannot find A in the environ
-     vector, but can still find B.  */
   env.set ("GDB_SELFTEST_ENVIRON_1", "aaa");
   SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_1"), "aaa") == 0);
+  /* User-set environ var list must contain one element.  */
+  SELF_CHECK (env.user_set_env ().size () == 1);
+  SELF_CHECK (set_contains (env.user_set_env (),
+                           std::string ("GDB_SELFTEST_ENVIRON_1=aaa")));
 
   env.set ("GDB_SELFTEST_ENVIRON_2", "bbb");
   SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
   SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON_1") == NULL);
   SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
 
+  /* The user-set environ var list must contain only one element
+     now.  */
+  SELF_CHECK (set_contains (env.user_set_env (),
+                           std::string ("GDB_SELFTEST_ENVIRON_2=bbb")));
+  SELF_CHECK (env.user_set_env ().size () == 1);
+}
+
+/* Check if unset followed by a set in an empty vector works.  */
+
+static void
+test_unset_set_empty_vector ()
+{
+  gdb_environ env;
+
+  env.set ("PWD", "test");
+  SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0);
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("PWD=test")));
+  SELF_CHECK (env.user_unset_env ().size () == 0);
+  /* The second element must be NULL.  */
+  SELF_CHECK (env.envp ()[1] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 1);
+  env.unset ("PWD");
+  SELF_CHECK (env.envp ()[0] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (env.user_unset_env ().size () == 1);
+  SELF_CHECK (set_contains (env.user_unset_env (), std::string ("PWD")));
+}
+
+/* When we clear our environ vector, there should be only one
+   element on it (NULL), and we shouldn't be able to get our test
+   variable.  */
+
+static void
+test_vector_clear ()
+{
+  gdb_environ env;
+
+  env.set (gdb_selftest_env_var, "1");
+
   env.clear ();
+  SELF_CHECK (env.envp ()[0] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (env.user_unset_env ().size () == 0);
+  SELF_CHECK (env.get (gdb_selftest_env_var) == NULL);
+}
+
+/* Test that after a std::move the moved-from object is left at a
+   valid state (i.e., its only element is NULL).  */
+
+static void
+test_std_move ()
+{
+  gdb_environ env;
+  gdb_environ env2;
 
-  /* Test that after a std::move the moved-from object is left at a
-     valid state (i.e., its only element is NULL).  */
   env.set ("A", "1");
   SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
-  gdb_environ env2;
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
+  SELF_CHECK (env.user_set_env ().size () == 1);
+
   env2 = std::move (env);
   SELF_CHECK (env.envp ()[0] == NULL);
   SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
   SELF_CHECK (env2.envp ()[1] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1")));
+  SELF_CHECK (env2.user_set_env ().size () == 1);
   env.set ("B", "2");
   SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
   SELF_CHECK (env.envp ()[1] == NULL);
+}
+
+/* Test that the move constructor leaves everything at a valid
+   state.  */
+
+static void
+test_move_constructor ()
+{
+  gdb_environ env;
 
-  /* Test that the move constructor leaves everything at a valid
-     state.  */
-  env.clear ();
   env.set ("A", "1");
   SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
-  gdb_environ env3 = std::move (env);
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
+
+  gdb_environ env2 = std::move (env);
   SELF_CHECK (env.envp ()[0] == NULL);
-  SELF_CHECK (strcmp (env3.get ("A"), "1") == 0);
-  SELF_CHECK (env3.envp ()[1] == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
+  SELF_CHECK (env2.envp ()[1] == NULL);
+  SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1")));
+  SELF_CHECK (env2.user_set_env ().size () == 1);
+
   env.set ("B", "2");
   SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
   SELF_CHECK (env.envp ()[1] == NULL);
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("B=2")));
+  SELF_CHECK (env.user_set_env ().size () == 1);
+}
+
+/* Test that self-moving works.  */
+
+static void
+test_self_move ()
+{
+  gdb_environ env;
 
   /* Test self-move.  */
-  env.clear ();
   env.set ("A", "1");
   SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
+  SELF_CHECK (env.user_set_env ().size () == 1);
 
   /* Some compilers warn about moving to self, but that's precisely what we want
      to test here, so turn this warning off.  */
   SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
   SELF_CHECK (strcmp (env.envp ()[0], "A=1") == 0);
   SELF_CHECK (env.envp ()[1] == NULL);
+  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
+  SELF_CHECK (env.user_set_env ().size () == 1);
+}
+
+/* Test if set/unset/reset works.  */
+
+static void
+test_set_unset_reset ()
+{
+  gdb_environ env = gdb_environ::from_host_environ ();
+
+  /* Our test variable should already be present in the host's environment.  */
+  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") != NULL);
+
+  /* Set our test variable to another value.  */
+  env.set ("GDB_SELFTEST_ENVIRON", "test");
+  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "test") == 0);
+  SELF_CHECK (env.user_set_env ().size () == 1);
+  SELF_CHECK (env.user_unset_env ().size () == 0);
+
+  /* And unset our test variable.  The variable still exists in the
+     host's environment, but doesn't exist in our vector.  */
+  env.unset ("GDB_SELFTEST_ENVIRON");
+  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
+  SELF_CHECK (env.user_set_env ().size () == 0);
+  SELF_CHECK (env.user_unset_env ().size () == 1);
+  SELF_CHECK (set_contains (env.user_unset_env (),
+                           std::string ("GDB_SELFTEST_ENVIRON")));
+
+  /* Re-set the test variable.  */
+  env.set ("GDB_SELFTEST_ENVIRON", "1");
+  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
+}
+
+static void
+run_tests ()
+{
+  /* Set a test environment variable.  */
+  if (setenv ("GDB_SELFTEST_ENVIRON", "1", 1) != 0)
+    error (_("Could not set environment variable for testing."));
+
+  test_vector_initialization ();
+
+  test_unset_set_empty_vector ();
+
+  test_init_from_host_environ ();
+
+  test_set_unset_reset ();
+
+  test_vector_clear ();
+
+  test_reinit_from_host_environ ();
+
+  /* Get rid of our test variable.  We won't need it anymore.  */
+  unsetenv ("GDB_SELFTEST_ENVIRON");
+
+  test_set_A_unset_B_unset_A_cannot_find_A_can_find_B ();
+
+  test_std_move ();
+
+  test_move_constructor ();
+
+  test_self_move ();
 }
 } /* namespace gdb_environ */
 } /* namespace selftests */