From: Andrew Burgess Date: Sat, 2 Nov 2024 17:35:14 +0000 (+0000) Subject: gdb: split up construct_inferior_arguments X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=512ca2fca4b;p=thirdparty%2Fbinutils-gdb.git gdb: split up construct_inferior_arguments The function construct_inferior_arguments (gdbsupport/common-inferior.cc) currently escapes all special shell characters. After this commit there will be two "levels" of quoting: 1. The current "full" quoting, where all posix shell special characters are quoted, and 2. a new "reduced" quoting, where only the characters that GDB sees as special (quotes and whitespace) are quoted. After this, almost all construct_inferior_arguments calls will use the "full" quoting, which is the current quoting. The "reduced" quoting will be used in this commit to restore the behaviour that was lost in the previous commit (more details below). In the future, the reduced quoting will be useful for some additional inferior argument that I have planned. I already posted my full inferior argument work here: https://inbox.sourceware.org/gdb-patches/cover.1730731085.git.aburgess@redhat.com But that series is pretty long, and wasn't getting reviewed, so I'm posted the series in parts now. Before the previous commit, GDB behaved like this: $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "$FOO". Notice that with 'startup-with-shell' off, the argument was left as just '$FOO'. But after the previous commit, this changed to: $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "\$FOO". Now the '$' is escaped with a backslash. This commit restores the original behaviour, as this is (currently) the only way to unquoted shell special characters into arguments from the GDB command line. The series that I listed above includes a new command line option for GDB which provides a better approach for controlling the quoting of special shell characters, but that work requires these patches to be merged first. I've split out the core of construct_inferior_arguments into the new function escape_characters, which takes a set of characters to escape. Then the two functions escape_shell_characters and escape_gdb_characters call escape_characters with the appropriate character sets. Finally, construct_inferior_arguments, now takes a boolean which indicates if we should perform full shell escaping, or just perform the reduced escaping. I've updated all uses of construct_inferior_arguments to pass a suitable value to indicate what escaping to perform (mostly just 'true', but one case in main.c is different), also I've updated inferior::set_args to take the same boolean flag, and pass it through to construct_inferior_arguments. Tested-By: Guinevere Larsen --- diff --git a/gdb/corelow.c b/gdb/corelow.c index 4662b5c6fc7..567ecd5c571 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -1174,7 +1174,7 @@ core_target_open (const char *arg, int from_tty) for (const gdb::unique_xmalloc_ptr &a : ctx.args ()) argv.push_back (a.get ()); gdb::array_view view (argv.data (), argv.size ()); - current_inferior ()->set_args (view); + current_inferior ()->set_args (view, true); /* And now copy the environment. */ current_inferior ()->environment = ctx.environment (); diff --git a/gdb/inferior.c b/gdb/inferior.c index 67d70c5c2fb..6472d49616c 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -167,9 +167,10 @@ inferior::tty () /* See inferior.h. */ void -inferior::set_args (gdb::array_view args) +inferior::set_args (gdb::array_view args, + bool escape_shell_char) { - set_args (construct_inferior_arguments (args)); + set_args (construct_inferior_arguments (args, escape_shell_char)); } void diff --git a/gdb/inferior.h b/gdb/inferior.h index 3d9f86c0d4a..327a474380f 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -522,8 +522,11 @@ public: m_args = std::move (args); }; - /* Set the argument string from some strings. */ - void set_args (gdb::array_view args); + /* Set the argument string from some strings in ARGS. When + ESCAPE_SHELL_CHAR is true all special shell characters in ARGS are + escaped, When false only the characters that GDB sees as special will + be escaped. See construct_inferior_arguments for more details. */ + void set_args (gdb::array_view args, bool escape_shell_char); /* Get the argument string to use when running this inferior. diff --git a/gdb/main.c b/gdb/main.c index 27043b74885..d126e98e16f 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -1078,7 +1078,8 @@ captured_main_1 (struct captured_main_args *context) execarg = argv[optind]; ++optind; current_inferior ()->set_args - (gdb::array_view (&argv[optind], argc - optind)); + (gdb::array_view (&argv[optind], argc - optind), + startup_with_shell); } else { diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index d11ca9e6506..356961cfbcb 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -929,7 +929,7 @@ infpy_set_args (PyObject *self, PyObject *value, void *closure) for (const auto &arg : args) argvec.push_back (arg.get ()); gdb::array_view view (argvec.data (), argvec.size ()); - inf->inferior->set_args (view); + inf->inferior->set_args (view, true); } else { diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 3d452f91686..def01c1ee80 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -3465,7 +3465,7 @@ handle_v_run (char *own_buf) else program_path.set (new_program_name.get ()); - program_args = construct_inferior_arguments (new_argv.get ()); + program_args = construct_inferior_arguments (new_argv.get (), true); try { @@ -4355,7 +4355,7 @@ captured_main (int argc, char *argv[]) int n = argc - (next_arg - argv); program_args - = construct_inferior_arguments ({&next_arg[1], &next_arg[n]}); + = construct_inferior_arguments ({&next_arg[1], &next_arg[n]}, true); /* Wait till we are at first instruction in program. */ target_create_inferior (program_path.get (), program_args); diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index 8e35f416e70..4b868294062 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -24,74 +24,131 @@ bool startup_with_shell = true; -/* See common-inferior.h. */ +/* Escape characters in ARG and return an updated string. The string + SPECIAL contains the set of characters that must be escaped. SPECIAL + must not be nullptr, and it is assumed that SPECIAL contains the newline + '\n' character. It is assumed that ARG is not nullptr, but ARG can + be the empty string. */ -std::string -construct_inferior_arguments (gdb::array_view argv) +static std::string +escape_characters (const char *arg, const char *special) { + gdb_assert (special != nullptr); + gdb_assert (arg != nullptr); + std::string result; #ifdef __MINGW32__ - /* This holds all the characters considered special to the - Windows shells. */ - static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; static const char quote = '"'; #else - /* This holds all the characters considered special to the - typical Unix shells. We include `^' because the SunOS - /bin/sh treats it as a synonym for `|'. */ - static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; static const char quote = '\''; #endif - for (int i = 0; i < argv.size (); ++i) + + /* Need to handle empty arguments specially. */ + if (arg[0] == '\0') { - if (i > 0) - result += ' '; + result += quote; + result += quote; + } + /* The special character handling code here assumes that if SPECIAL is + not nullptr, then SPECIAL will contain '\n'. This is true for all our + current usages, but if this ever changes in the future the following + might need reworking. */ + else + { +#ifdef __MINGW32__ + bool quoted = false; - /* Need to handle empty arguments specially. */ - if (argv[i][0] == '\0') + if (strpbrk (argv[i], special)) { - result += quote; + quoted = true; result += quote; } - else +#endif + for (const char *cp = arg; *cp; ++cp) { -#ifdef __MINGW32__ - bool quoted = false; - - if (strpbrk (argv[i], special)) + if (*cp == '\n') { - quoted = true; + /* A newline cannot be quoted with a backslash (it just + disappears), only by putting it inside quotes. */ + result += quote; + result += '\n'; result += quote; } -#endif - for (char *cp = argv[i]; *cp != '\0'; ++cp) + else { - if (*cp == '\n') - { - /* A newline cannot be quoted with a backslash (it - just disappears), only by putting it inside - quotes. */ - result += quote; - result += '\n'; - result += quote; - } - else - { #ifdef __MINGW32__ - if (*cp == quote) + if (*cp == quote) #else - if (strchr (special, *cp) != NULL) + if (strchr (special, *cp) != nullptr) #endif - result += '\\'; - result += *cp; - } + result += '\\'; + result += *cp; } + } #ifdef __MINGW32__ - if (quoted) - result += quote; + if (quoted) + result += quote; #endif - } + } + + return result; +} + +/* Return a version of ARG that has special shell characters escaped. */ + +static std::string +escape_shell_characters (const char *arg) +{ +#ifdef __MINGW32__ + /* This holds all the characters considered special to the + Windows shells. */ + static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n"; +#else + /* This holds all the characters considered special to the + typical Unix shells. We include `^' because the SunOS + /bin/sh treats it as a synonym for `|'. */ + static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n"; +#endif + + return escape_characters (arg, special); +} + +/* Return a version of ARG that has quote characters and white space + characters escaped. These are the characters that GDB sees as special + when splitting a string into separate arguments. */ + +static std::string +escape_gdb_characters (const char * arg) +{ +#ifdef __MINGW32__ + static const char special[] = "\" \t\n"; +#else + static const char special[] = "\"' \t\n"; +#endif + + return escape_characters (arg, special); +} + +/* See common-inferior.h. */ + +std::string +construct_inferior_arguments (gdb::array_view argv, + bool escape_shell_char) +{ + /* Select the desired escape function. */ + const auto escape_func = (escape_shell_char + ? escape_shell_characters + : escape_gdb_characters); + + std::string result; + + for (const char *a : argv) + { + if (!result.empty ()) + result += " "; + + result += escape_func (a); } return result; diff --git a/gdbsupport/common-inferior.h b/gdbsupport/common-inferior.h index ef998158945..3e8ec10df9b 100644 --- a/gdbsupport/common-inferior.h +++ b/gdbsupport/common-inferior.h @@ -52,9 +52,13 @@ extern const std::string &get_inferior_cwd (); the target is started up with a shell. */ extern bool startup_with_shell; -/* Compute command-line string given argument vector. This does the - same shell processing as fork_inferior. */ +/* Combine elements of ARGV into a single string, placing a single + whitespace character between each element. When ESCAPE_SHELL_CHAR is + true then any special shell characters in elemets of ARGV will be + escaped. When ESCAPE_SHELL_CHAR is false only the characters that GDB + sees as special (quotes and whitespace) are escaped. */ extern std::string -construct_inferior_arguments (gdb::array_view); +construct_inferior_arguments (gdb::array_view argv, + bool escape_shell_char); #endif /* GDBSUPPORT_COMMON_INFERIOR_H */