/* Top level stuff for GDB, the GNU debugger.
- Copyright (C) 1986-2020 Free Software Foundation, Inc.
+ Copyright (C) 1986-2021 Free Software Foundation, Inc.
This file is part of GDB.
#endif
#include "gdbsupport/alt-stack.h"
#include "observable.h"
+#include "serial.h"
/* The selected interpreter. This will be used as a set command
variable, so it should always be malloc'ed - since
otherwise. */
static std::string
-relocate_gdbinit_path_maybe_in_datadir (const std::string &file,
- bool relocatable)
+relocate_file_path_maybe_in_datadir (const std::string &file,
+ bool relocatable)
{
size_t datadir_len = strlen (GDB_DATADIR);
return relocated_path;
}
-/* Compute the locations of init files that GDB should source and
- return them in SYSTEM_GDBINIT, HOME_GDBINIT, LOCAL_GDBINIT. If
- there is no system gdbinit (resp. home gdbinit and local gdbinit)
- to be loaded, then SYSTEM_GDBINIT (resp. HOME_GDBINIT and
- LOCAL_GDBINIT) is set to the empty string. */
-static void
-get_init_files (std::vector<std::string> *system_gdbinit,
- std::string *home_gdbinit,
- std::string *local_gdbinit)
+/* A class to wrap up the logic for finding the three different types of
+ initialisation files GDB uses, system wide, home directory, and current
+ working directory. */
+
+class gdb_initfile_finder
{
- static std::vector<std::string> sysgdbinit;
- static std::string homeinit;
- static std::string localinit;
- static int initialized = 0;
+public:
+ /* Constructor. Finds initialisation files named FILENAME in the home
+ directory or local (current working) directory. System initialisation
+ files are found in both SYSTEM_FILENAME and SYSTEM_DIRNAME if these
+ are not nullptr (either or both can be). The matching *_RELOCATABLE
+ flag is passed through to RELOCATE_FILE_PATH_MAYBE_IN_DATADIR.
+
+ If FILENAME starts with a '.' then when looking in the home directory
+ this first '.' can be ignored in some cases. */
+ explicit gdb_initfile_finder (const char *filename,
+ const char *system_filename,
+ bool system_filename_relocatable,
+ const char *system_dirname,
+ bool system_dirname_relocatable,
+ bool lookup_local_file)
+ {
+ struct stat s;
- if (!initialized)
- {
- struct stat homebuf, cwdbuf, s;
+ if (system_filename != nullptr && system_filename[0] != '\0')
+ {
+ std::string relocated_filename
+ = relocate_file_path_maybe_in_datadir (system_filename,
+ system_filename_relocatable);
+ if (!relocated_filename.empty ()
+ && stat (relocated_filename.c_str (), &s) == 0)
+ m_system_files.push_back (relocated_filename);
+ }
- if (SYSTEM_GDBINIT[0])
- {
- std::string relocated_sysgdbinit
- = relocate_gdbinit_path_maybe_in_datadir
- (SYSTEM_GDBINIT, SYSTEM_GDBINIT_RELOCATABLE);
- if (!relocated_sysgdbinit.empty ()
- && stat (relocated_sysgdbinit.c_str (), &s) == 0)
- sysgdbinit.push_back (relocated_sysgdbinit);
- }
- if (SYSTEM_GDBINIT_DIR[0])
- {
- std::string relocated_gdbinit_dir
- = relocate_gdbinit_path_maybe_in_datadir
- (SYSTEM_GDBINIT_DIR, SYSTEM_GDBINIT_DIR_RELOCATABLE);
- if (!relocated_gdbinit_dir.empty ()) {
- gdb_dir_up dir (opendir (relocated_gdbinit_dir.c_str ()));
+ if (system_dirname != nullptr && system_dirname[0] != '\0')
+ {
+ std::string relocated_dirname
+ = relocate_file_path_maybe_in_datadir (system_dirname,
+ system_dirname_relocatable);
+ if (!relocated_dirname.empty ())
+ {
+ gdb_dir_up dir (opendir (relocated_dirname.c_str ()));
if (dir != nullptr)
{
std::vector<std::string> files;
- for (;;)
+ while (true)
{
struct dirent *ent = readdir (dir.get ());
if (ent == nullptr)
std::string name (ent->d_name);
if (name == "." || name == "..")
continue;
- /* ent->d_type is not available on all systems (e.g. mingw,
- Solaris), so we have to call stat(). */
- std::string filename
- = relocated_gdbinit_dir + SLASH_STRING + name;
- if (stat (filename.c_str (), &s) != 0
+ /* ent->d_type is not available on all systems
+ (e.g. mingw, Solaris), so we have to call stat(). */
+ std::string tmp_filename
+ = relocated_dirname + SLASH_STRING + name;
+ if (stat (tmp_filename.c_str (), &s) != 0
|| !S_ISREG (s.st_mode))
continue;
const struct extension_language_defn *extlang
- = get_ext_lang_of_file (filename.c_str ());
+ = get_ext_lang_of_file (tmp_filename.c_str ());
/* We effectively don't support "set script-extension
- off/soft", because we are loading system init files here,
- so it does not really make sense to depend on a
- setting. */
+ off/soft", because we are loading system init files
+ here, so it does not really make sense to depend on
+ a setting. */
if (extlang != nullptr && ext_lang_present_p (extlang))
- files.push_back (std::move (filename));
+ files.push_back (std::move (tmp_filename));
}
std::sort (files.begin (), files.end ());
- sysgdbinit.insert (sysgdbinit.end (),
- files.begin (), files.end ());
+ m_system_files.insert (m_system_files.end (),
+ files.begin (), files.end ());
}
}
- }
-
- const char *homedir = getenv ("HOME");
+ }
/* If the .gdbinit file in the current directory is the same as
the $HOME/.gdbinit file, it should not be sourced. homebuf
are zero in case one of them fails (this guarantees that they
won't match if either exists). */
- memset (&homebuf, 0, sizeof (struct stat));
- memset (&cwdbuf, 0, sizeof (struct stat));
+ struct stat homebuf, cwdbuf;
+ memset (&homebuf, 0, sizeof (struct stat));
+ memset (&cwdbuf, 0, sizeof (struct stat));
- if (homedir)
- {
- homeinit = std::string (homedir) + SLASH_STRING + GDBINIT;
- if (stat (homeinit.c_str (), &homebuf) != 0)
- {
- homeinit = "";
- }
- }
+ m_home_file = find_gdb_home_config_file (filename, &homebuf);
- if (stat (GDBINIT, &cwdbuf) == 0)
- {
- if (homeinit.empty ()
- || memcmp ((char *) &homebuf, (char *) &cwdbuf,
- sizeof (struct stat)))
- localinit = GDBINIT;
- }
-
- initialized = 1;
- }
+ if (lookup_local_file && stat (filename, &cwdbuf) == 0)
+ {
+ if (m_home_file.empty ()
+ || memcmp ((char *) &homebuf, (char *) &cwdbuf,
+ sizeof (struct stat)))
+ m_local_file = filename;
+ }
+ }
+
+ DISABLE_COPY_AND_ASSIGN (gdb_initfile_finder);
+
+ /* Return a list of system initialisation files. The list could be
+ empty. */
+ const std::vector<std::string> &system_files () const
+ { return m_system_files; }
+
+ /* Return the path to the home initialisation file. The string can be
+ empty if there is no such file. */
+ const std::string &home_file () const
+ { return m_home_file; }
+
+ /* Return the path to the local initialisation file. The string can be
+ empty if there is no such file. */
+ const std::string &local_file () const
+ { return m_local_file; }
+
+private:
+
+ /* Vector of all system init files in the order they should be processed.
+ Could be empty. */
+ std::vector<std::string> m_system_files;
+
+ /* Initialization file from the home directory. Could be the empty
+ string if there is no such file found. */
+ std::string m_home_file;
+
+ /* Initialization file from the current working directory. Could be the
+ empty string if there is no such file found. */
+ std::string m_local_file;
+};
+
+/* Compute the locations of init files that GDB should source and return
+ them in SYSTEM_GDBINIT, HOME_GDBINIT, LOCAL_GDBINIT. The SYSTEM_GDBINIT
+ can be returned as an empty vector, and HOME_GDBINIT and LOCAL_GDBINIT
+ can be returned as empty strings if there is no init file of that
+ type. */
+
+static void
+get_init_files (std::vector<std::string> *system_gdbinit,
+ std::string *home_gdbinit,
+ std::string *local_gdbinit)
+{
+ /* Cache the file lookup object so we only actually search for the files
+ once. */
+ static gdb::optional<gdb_initfile_finder> init_files;
+ if (!init_files.has_value ())
+ init_files.emplace (GDBINIT, SYSTEM_GDBINIT, SYSTEM_GDBINIT_RELOCATABLE,
+ SYSTEM_GDBINIT_DIR, SYSTEM_GDBINIT_DIR_RELOCATABLE,
+ true);
+
+ *system_gdbinit = init_files->system_files ();
+ *home_gdbinit = init_files->home_file ();
+ *local_gdbinit = init_files->local_file ();
+}
+
+/* Compute the location of the early init file GDB should source and return
+ it in HOME_GDBEARLYINIT. HOME_GDBEARLYINIT could be returned as an
+ empty string if there is no early init file found. */
+
+static void
+get_earlyinit_files (std::string *home_gdbearlyinit)
+{
+ /* Cache the file lookup object so we only actually search for the files
+ once. */
+ static gdb::optional<gdb_initfile_finder> init_files;
+ if (!init_files.has_value ())
+ init_files.emplace (GDBEARLYINIT, nullptr, false, nullptr, false, false);
- *system_gdbinit = sysgdbinit;
- *home_gdbinit = homeinit;
- *local_gdbinit = localinit;
+ *home_gdbearlyinit = init_files->home_file ();
}
/* Start up the event loop. This is the entry point to the event loop
static int
catch_command_errors (catch_command_errors_const_ftype command,
- const char *arg, int from_tty)
+ const char *arg, int from_tty,
+ bool do_bp_actions = false)
{
try
{
command (arg, from_tty);
maybe_wait_sync_command_done (was_sync);
+
+ /* Do any commands attached to breakpoint we stopped at. */
+ if (do_bp_actions)
+ bpstat_do_actions ();
}
catch (const gdb_exception &e)
{
/* Option type -ix. */
CMDARG_INIT_FILE,
-
+
/* Option type -iex. */
- CMDARG_INIT_COMMAND
+ CMDARG_INIT_COMMAND,
+
+ /* Option type -eix. */
+ CMDARG_EARLYINIT_FILE,
+
+ /* Option type -eiex. */
+ CMDARG_EARLYINIT_COMMAND
};
/* Arguments of --command option and its counterpart. */
char *string;
};
+/* From CMDARG_VEC execute command files (matching FILE_TYPE) or commands
+ (matching CMD_TYPE). Update the value in *RET if and scripts or
+ commands are executed. */
+
+static void
+execute_cmdargs (const std::vector<struct cmdarg> *cmdarg_vec,
+ cmdarg_kind file_type, cmdarg_kind cmd_type,
+ int *ret)
+{
+ for (const auto &cmdarg_p : *cmdarg_vec)
+ {
+ if (cmdarg_p.type == file_type)
+ *ret = catch_command_errors (source_script, cmdarg_p.string,
+ !batch_flag);
+ else if (cmdarg_p.type == cmd_type)
+ *ret = catch_command_errors (execute_command, cmdarg_p.string,
+ !batch_flag, true);
+ }
+}
+
static void
captured_main_1 (struct captured_main_args *context)
{
OPT_WINDOWS,
OPT_IX,
OPT_IEX,
+ OPT_EIX,
+ OPT_EIEX,
OPT_READNOW,
OPT_READNEVER
};
{"init-eval-command", required_argument, 0, OPT_IEX},
{"ix", required_argument, 0, OPT_IX},
{"iex", required_argument, 0, OPT_IEX},
+ {"early-init-command", required_argument, 0, OPT_EIX},
+ {"early-init-eval-command", required_argument, 0, OPT_EIEX},
+ {"eix", required_argument, 0, OPT_EIX},
+ {"eiex", required_argument, 0, OPT_EIEX},
#ifdef GDBTK
{"tclcommand", required_argument, 0, 'z'},
{"enable-external-editor", no_argument, 0, 'y'},
case OPT_IEX:
cmdarg_vec.emplace_back (CMDARG_INIT_COMMAND, optarg);
break;
+ case OPT_EIX:
+ cmdarg_vec.emplace_back (CMDARG_EARLYINIT_FILE, optarg);
+ break;
+ case OPT_EIEX:
+ cmdarg_vec.emplace_back (CMDARG_EARLYINIT_COMMAND, optarg);
+ break;
case 'B':
batch_flag = batch_silent = 1;
gdb_stdout = new null_file ();
gdb::alternate_signal_stack signal_stack;
/* Initialize all files. */
- gdb_init (gdb_program_name);
+ gdb_init ();
+
+ /* Process early init files and early init options from the command line. */
+ if (!inhibit_gdbinit)
+ {
+ std::string home_gdbearlyinit;
+ get_earlyinit_files (&home_gdbearlyinit);
+ if (!home_gdbearlyinit.empty () && !inhibit_home_gdbinit)
+ ret = catch_command_errors (source_script,
+ home_gdbearlyinit.c_str (), 0);
+ }
+ execute_cmdargs (&cmdarg_vec, CMDARG_EARLYINIT_FILE,
+ CMDARG_EARLYINIT_COMMAND, &ret);
+
+ /* Initialize the extension languages. */
+ ext_lang_initialization ();
+
+ /* Recheck if we're starting up quietly after processing the startup
+ scripts and commands. */
+ if (!quiet)
+ quiet = check_quiet_mode ();
/* Now that gdb_init has created the initial inferior, we're in
position to set args for that inferior. */
ret = catch_command_errors (source_script, home_gdbinit.c_str (), 0);
/* Process '-ix' and '-iex' options early. */
- for (i = 0; i < cmdarg_vec.size (); i++)
- {
- const struct cmdarg &cmdarg_p = cmdarg_vec[i];
-
- switch (cmdarg_p.type)
- {
- case CMDARG_INIT_FILE:
- ret = catch_command_errors (source_script, cmdarg_p.string,
- !batch_flag);
- break;
- case CMDARG_INIT_COMMAND:
- ret = catch_command_errors (execute_command, cmdarg_p.string,
- !batch_flag);
- break;
- }
- }
+ execute_cmdargs (&cmdarg_vec, CMDARG_INIT_FILE, CMDARG_INIT_COMMAND, &ret);
/* Now perform all the actions indicated by the arguments. */
if (cdarg != NULL)
auto_load_local_gdbinit_pathname
= gdb_realpath (local_gdbinit.c_str ()).release ();
- if (!inhibit_gdbinit && auto_load_local_gdbinit
- && file_is_auto_load_safe (local_gdbinit.c_str (),
- _("auto-load: Loading .gdbinit "
- "file \"%s\".\n"),
- local_gdbinit.c_str ()))
+ if (!inhibit_gdbinit && auto_load_local_gdbinit)
{
- auto_load_local_gdbinit_loaded = 1;
+ auto_load_debug_printf ("Loading .gdbinit file \"%s\".",
+ local_gdbinit.c_str ());
- ret = catch_command_errors (source_script, local_gdbinit.c_str (), 0);
+ if (file_is_auto_load_safe (local_gdbinit.c_str ()))
+ {
+ auto_load_local_gdbinit_loaded = 1;
+
+ ret = catch_command_errors (source_script, local_gdbinit.c_str (), 0);
+ }
}
}
load_auto_scripts_for_objfile (objfile);
/* Process '-x' and '-ex' options. */
- for (i = 0; i < cmdarg_vec.size (); i++)
- {
- const struct cmdarg &cmdarg_p = cmdarg_vec[i];
-
- switch (cmdarg_p.type)
- {
- case CMDARG_FILE:
- ret = catch_command_errors (source_script, cmdarg_p.string,
- !batch_flag);
- break;
- case CMDARG_COMMAND:
- ret = catch_command_errors (execute_command, cmdarg_p.string,
- !batch_flag);
- break;
- }
- }
+ execute_cmdargs (&cmdarg_vec, CMDARG_FILE, CMDARG_COMMAND, &ret);
/* Read in the old history after all the command files have been
read. */
std::vector<std::string> system_gdbinit;
std::string home_gdbinit;
std::string local_gdbinit;
+ std::string home_gdbearlyinit;
get_init_files (&system_gdbinit, &home_gdbinit, &local_gdbinit);
+ get_earlyinit_files (&home_gdbearlyinit);
/* Note: The options in the list below are only approximately sorted
in the alphabetical order, so as to group closely related options
"), stream);
fputs_unfiltered (_("\
Selection of debuggee and its files:\n\n\
- --args Arguments after executable-file are passed to inferior\n\
+ --args Arguments after executable-file are passed to inferior.\n\
--core=COREFILE Analyze the core dump COREFILE.\n\
--exec=EXECFILE Use EXECFILE as the executable.\n\
--pid=PID Attach to running process PID.\n\
Output and user interface control:\n\n\
--fullname Output information used by emacs-GDB interface.\n\
--interpreter=INTERP\n\
- Select a specific interpreter / user interface\n\
+ Select a specific interpreter / user interface.\n\
--tty=TTY Use TTY for input/output by the program being debugged.\n\
-w Use the GUI interface.\n\
--nw Do not use the GUI interface.\n\
Set GDB's data-directory to DIR.\n\
"), stream);
fputs_unfiltered (_("\n\
+At startup, GDB reads the following early init files and executes their\n\
+commands:\n\
+"), stream);
+ if (!home_gdbearlyinit.empty ())
+ fprintf_unfiltered (stream, _("\
+ * user-specific early init file: %s\n\
+"), home_gdbearlyinit.c_str ());
+ if (home_gdbearlyinit.empty ())
+ fprintf_unfiltered (stream, _("\
+ None found.\n"));
+ fputs_unfiltered (_("\n\
At startup, GDB reads the following init files and executes their commands:\n\
"), stream);
if (!system_gdbinit.empty ())
fprintf_unfiltered (stream, _("\
* local init file (see also 'set auto-load local-gdbinit'): ./%s\n\
"), local_gdbinit.c_str ());
+ if (system_gdbinit.empty () && home_gdbinit.empty ()
+ && local_gdbinit.empty ())
+ fprintf_unfiltered (stream, _("\
+ None found.\n"));
fputs_unfiltered (_("\n\
For more information, type \"help\" from within GDB, or consult the\n\
GDB manual (available as on-line info or a printed manual).\n\