X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fauto-load.c;h=99bd96b971af98d67f19893fcf13ea059cc9ece0;hb=e286671bf99ab870d67431068e863c1c57631b1f;hp=5ee117f13dfb2cf70a8b35ce90208ad74711763e;hpb=7349ff92c20df6a9f4ea3e53383d99622960d74d;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/auto-load.c b/gdb/auto-load.c index 5ee117f13d..99bd96b971 100644 --- a/gdb/auto-load.c +++ b/gdb/auto-load.c @@ -1,6 +1,6 @@ /* GDB routines for supporting auto-loaded scripts. - Copyright (C) 2012 Free Software Foundation, Inc. + Copyright (C) 2012-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -18,34 +18,51 @@ along with this program. If not, see . */ #include "defs.h" +#include #include "auto-load.h" #include "progspace.h" -#include "python/python.h" #include "gdb_regex.h" #include "ui-out.h" #include "filenames.h" #include "command.h" -#include "observer.h" +#include "observable.h" #include "objfiles.h" -#include "exceptions.h" #include "cli/cli-script.h" #include "gdbcmd.h" +#include "cli/cli-cmds.h" #include "cli/cli-decode.h" #include "cli/cli-setshow.h" -#include "gdb_vecs.h" #include "readline/tilde.h" #include "completer.h" -#include "observer.h" - -/* The suffix of per-objfile scripts to auto-load as non-Python command files. - E.g. When the program loads libfoo.so, look for libfoo-gdb.gdb. */ -#define GDB_AUTO_FILE_NAME "-gdb.gdb" - -static void source_gdb_script_for_objfile (struct objfile *objfile, FILE *file, - const char *filename); +#include "fnmatch.h" +#include "top.h" +#include "gdbsupport/filestuff.h" +#include "extension.h" +#include "gdb/section-scripts.h" +#include +#include "gdbsupport/pathstuff.h" +#include "cli/cli-style.h" + +/* The section to look in for auto-loaded scripts (in file formats that + support sections). + Each entry in this section is a record that begins with a leading byte + identifying the record type. + At the moment we only support one record type: A leading byte of 1, + followed by the path of a python script to load. */ +#define AUTO_SECTION_NAME ".debug_gdb_scripts" + +static void maybe_print_unsupported_script_warning + (struct auto_load_pspace_info *, struct objfile *objfile, + const struct extension_language_defn *language, + const char *section_name, unsigned offset); + +static void maybe_print_script_not_found_warning + (struct auto_load_pspace_info *, struct objfile *objfile, + const struct extension_language_defn *language, + const char *section_name, unsigned offset); /* Value of the 'set debug auto-load' configuration variable. */ -static int debug_auto_load = 0; +static bool debug_auto_load = false; /* "show" command for the debug_auto_load configuration variable. */ @@ -63,7 +80,7 @@ show_debug_auto_load (struct ui_file *file, int from_tty, set auto-load gdb-scripts on|off This is true if we should auto-load associated scripts when an objfile is opened, false otherwise. */ -static int auto_load_gdb_scripts = 1; +static bool auto_load_gdb_scripts = true; /* "show" command for the auto_load_gdb_scripts configuration variable. */ @@ -76,6 +93,14 @@ show_auto_load_gdb_scripts (struct ui_file *file, int from_tty, value); } +/* Return non-zero if auto-loading gdb scripts is enabled. */ + +int +auto_load_gdb_scripts_enabled (const struct extension_language_defn *extlang) +{ + return auto_load_gdb_scripts; +} + /* Internal-use flag to enable/disable auto-loading. This is true if we should auto-load python code when an objfile is opened, false otherwise. @@ -86,16 +111,16 @@ show_auto_load_gdb_scripts (struct ui_file *file, int from_tty, This flag exists to facilitate deferring auto-loading during start-up until after ./.gdbinit has been read; it may augment the search directories used to find the scripts. */ -int global_auto_load = 1; +bool global_auto_load = true; /* Auto-load .gdbinit file from the current directory? */ -int auto_load_local_gdbinit = 1; +bool auto_load_local_gdbinit = true; /* Absolute pathname to the current directory .gdbinit, if it exists. */ char *auto_load_local_gdbinit_pathname = NULL; -/* Boolean value if AUTO_LOAD_LOCAL_GDBINIT_PATHNAME has been loaded. */ -int auto_load_local_gdbinit_loaded = 0; +/* if AUTO_LOAD_LOCAL_GDBINIT_PATHNAME has been loaded. */ +bool auto_load_local_gdbinit_loaded = false; /* "show" command for the auto_load_local_gdbinit configuration variable. */ @@ -116,7 +141,7 @@ static char *auto_load_dir; /* "set" command for the auto_load_dir configuration variable. */ static void -set_auto_load_dir (char *args, int from_tty, struct cmd_list_element *c) +set_auto_load_dir (const char *args, int from_tty, struct cmd_list_element *c) { /* Setting the variable to "" resets it to the compile time defaults. */ if (auto_load_dir[0] == '\0') @@ -145,74 +170,82 @@ static char *auto_load_safe_path; /* Vector of directory elements of AUTO_LOAD_SAFE_PATH with each one normalized by tilde_expand and possibly each entries has added its gdb_realpath counterpart. */ -static VEC (char_ptr) *auto_load_safe_path_vec; +std::vector> auto_load_safe_path_vec; + +/* Expand $datadir and $debugdir in STRING according to the rules of + substitute_path_component. */ + +static std::vector> +auto_load_expand_dir_vars (const char *string) +{ + char *s = xstrdup (string); + substitute_path_component (&s, "$datadir", gdb_datadir.c_str ()); + substitute_path_component (&s, "$debugdir", debug_file_directory); + + if (debug_auto_load && strcmp (s, string) != 0) + fprintf_unfiltered (gdb_stdlog, + _("auto-load: Expanded $-variables to \"%s\".\n"), s); + + std::vector> dir_vec + = dirnames_to_char_ptr_vec (s); + xfree(s); + + return dir_vec; +} /* Update auto_load_safe_path_vec from current AUTO_LOAD_SAFE_PATH. */ static void auto_load_safe_path_vec_update (void) { - VEC (char_ptr) *dir_vec = NULL; - unsigned len; - int ix; - if (debug_auto_load) fprintf_unfiltered (gdb_stdlog, _("auto-load: Updating directories of \"%s\".\n"), auto_load_safe_path); - free_char_ptr_vec (auto_load_safe_path_vec); - - auto_load_safe_path_vec = dirnames_to_char_ptr_vec (auto_load_safe_path); - len = VEC_length (char_ptr, auto_load_safe_path_vec); + auto_load_safe_path_vec = auto_load_expand_dir_vars (auto_load_safe_path); + size_t len = auto_load_safe_path_vec.size (); /* Apply tilde_expand and gdb_realpath to each AUTO_LOAD_SAFE_PATH_VEC element. */ - for (ix = 0; ix < len; ix++) + for (size_t i = 0; i < len; i++) { - char *dir = VEC_index (char_ptr, auto_load_safe_path_vec, ix); - char *ddir_subst, *expanded, *real_path; - - ddir_subst = xstrdup (dir); - substitute_path_component (&ddir_subst, "$ddir", gdb_datadir); - expanded = tilde_expand (ddir_subst); - xfree (ddir_subst); - real_path = gdb_realpath (expanded); + gdb::unique_xmalloc_ptr &in_vec = auto_load_safe_path_vec[i]; + gdb::unique_xmalloc_ptr expanded (tilde_expand (in_vec.get ())); + gdb::unique_xmalloc_ptr real_path = gdb_realpath (expanded.get ()); - /* Ensure the current entry is at least a valid path (therefore - $ddir-expanded and tilde-expanded). */ - VEC_replace (char_ptr, auto_load_safe_path_vec, ix, expanded); + /* Ensure the current entry is at least tilde_expand-ed. ORIGINAL makes + sure we free the original string. */ + gdb::unique_xmalloc_ptr original = std::move (in_vec); + in_vec = std::move (expanded); if (debug_auto_load) { - if (strcmp (expanded, dir) == 0) + if (strcmp (in_vec.get (), original.get ()) == 0) fprintf_unfiltered (gdb_stdlog, _("auto-load: Using directory \"%s\".\n"), - expanded); + in_vec.get ()); else fprintf_unfiltered (gdb_stdlog, _("auto-load: Resolved directory \"%s\" " "as \"%s\".\n"), - dir, expanded); + original.get (), in_vec.get ()); } - xfree (dir); /* If gdb_realpath returns a different content, append it. */ - if (strcmp (real_path, expanded) == 0) - xfree (real_path); - else + if (strcmp (real_path.get (), in_vec.get ()) != 0) { - VEC_safe_push (char_ptr, auto_load_safe_path_vec, real_path); - if (debug_auto_load) fprintf_unfiltered (gdb_stdlog, _("auto-load: And canonicalized as \"%s\".\n"), - real_path); + real_path.get ()); + + auto_load_safe_path_vec.push_back (std::move (real_path)); } } } -/* Variable gdb_datadir has been set. Update content depending on $ddir. */ +/* Variable gdb_datadir has been set. Update content depending on $datadir. */ static void auto_load_gdb_datadir_changed (void) @@ -223,7 +256,8 @@ auto_load_gdb_datadir_changed (void) /* "set" command for the auto_load_safe_path configuration variable. */ static void -set_auto_load_safe_path (char *args, int from_tty, struct cmd_list_element *c) +set_auto_load_safe_path (const char *args, + int from_tty, struct cmd_list_element *c) { /* Setting the variable to "" resets it to the compile time defaults. */ if (auto_load_safe_path[0] == '\0') @@ -241,7 +275,15 @@ static void show_auto_load_safe_path (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) { - if (strcmp (value, "/") == 0) + const char *cs; + + /* Check if user has entered either "/" or for example ":". + But while more complicate content like ":/foo" would still also + permit any location do not hide those. */ + + for (cs = value; *cs && (*cs == DIRNAME_SEPARATOR || IS_DIR_SEPARATOR (*cs)); + cs++); + if (*cs == 0) fprintf_filtered (file, _("Auto-load files are safe to load from any " "directory.\n")); else @@ -254,7 +296,7 @@ show_auto_load_safe_path (struct ui_file *file, int from_tty, variable. */ static void -add_auto_load_safe_path (char *args, int from_tty) +add_auto_load_safe_path (const char *args, int from_tty) { char *s; @@ -271,71 +313,148 @@ Use 'set auto-load safe-path /' for disabling the auto-load safe-path security.\ auto_load_safe_path_vec_update (); } -/* Return 1 if FILENAME is equal to DIR or if FILENAME belongs to the - subdirectory DIR. Return 0 otherwise. gdb_realpath normalization is never - done here. */ +/* "add-auto-load-scripts-directory" command for the auto_load_dir configuration + variable. */ -static ATTRIBUTE_PURE int -filename_is_in_dir (const char *filename, const char *dir) +static void +add_auto_load_dir (const char *args, int from_tty) +{ + char *s; + + if (args == NULL || *args == 0) + error (_("Directory argument required.")); + + s = xstrprintf ("%s%c%s", auto_load_dir, DIRNAME_SEPARATOR, args); + xfree (auto_load_dir); + auto_load_dir = s; +} + +/* Implementation for filename_is_in_pattern overwriting the caller's FILENAME + and PATTERN. */ + +static int +filename_is_in_pattern_1 (char *filename, char *pattern) { - size_t dir_len = strlen (dir); + size_t pattern_len = strlen (pattern); + size_t filename_len = strlen (filename); - while (dir_len && IS_DIR_SEPARATOR (dir[dir_len - 1])) - dir_len--; + if (debug_auto_load) + fprintf_unfiltered (gdb_stdlog, _("auto-load: Matching file \"%s\" " + "to pattern \"%s\"\n"), + filename, pattern); + + /* Trim trailing slashes ("/") from PATTERN. Even for "d:\" paths as + trailing slashes are trimmed also from FILENAME it still matches + correctly. */ + while (pattern_len && IS_DIR_SEPARATOR (pattern[pattern_len - 1])) + pattern_len--; + pattern[pattern_len] = '\0'; /* Ensure auto_load_safe_path "/" matches any FILENAME. On MS-Windows platform FILENAME even after gdb_realpath does not have to start with IS_DIR_SEPARATOR character, such as the 'C:\x.exe' filename. */ - if (dir_len == 0) - return 1; + if (pattern_len == 0) + { + if (debug_auto_load) + fprintf_unfiltered (gdb_stdlog, + _("auto-load: Matched - empty pattern\n")); + return 1; + } + + for (;;) + { + /* Trim trailing slashes ("/"). PATTERN also has slashes trimmed the + same way so they will match. */ + while (filename_len && IS_DIR_SEPARATOR (filename[filename_len - 1])) + filename_len--; + filename[filename_len] = '\0'; + if (filename_len == 0) + { + if (debug_auto_load) + fprintf_unfiltered (gdb_stdlog, + _("auto-load: Not matched - pattern \"%s\".\n"), + pattern); + return 0; + } + + if (gdb_filename_fnmatch (pattern, filename, FNM_FILE_NAME | FNM_NOESCAPE) + == 0) + { + if (debug_auto_load) + fprintf_unfiltered (gdb_stdlog, _("auto-load: Matched - file " + "\"%s\" to pattern \"%s\".\n"), + filename, pattern); + return 1; + } - return (filename_ncmp (dir, filename, dir_len) == 0 - && (IS_DIR_SEPARATOR (filename[dir_len]) - || filename[dir_len] == '\0')); + /* Trim trailing FILENAME component. */ + while (filename_len > 0 && !IS_DIR_SEPARATOR (filename[filename_len - 1])) + filename_len--; + } +} + +/* Return 1 if FILENAME matches PATTERN or if FILENAME resides in + a subdirectory of a directory that matches PATTERN. Return 0 otherwise. + gdb_realpath normalization is never done here. */ + +static ATTRIBUTE_PURE int +filename_is_in_pattern (const char *filename, const char *pattern) +{ + char *filename_copy, *pattern_copy; + + filename_copy = (char *) alloca (strlen (filename) + 1); + strcpy (filename_copy, filename); + pattern_copy = (char *) alloca (strlen (pattern) + 1); + strcpy (pattern_copy, pattern); + + return filename_is_in_pattern_1 (filename_copy, pattern_copy); } /* Return 1 if FILENAME belongs to one of directory components of AUTO_LOAD_SAFE_PATH_VEC. Return 0 otherwise. auto_load_safe_path_vec_update is never called. - *FILENAME_REALP may be updated by gdb_realpath of FILENAME - it has to be - freed by the caller. */ + *FILENAME_REALP may be updated by gdb_realpath of FILENAME. */ static int filename_is_in_auto_load_safe_path_vec (const char *filename, - char **filename_realp) + gdb::unique_xmalloc_ptr *filename_realp) { - char *dir; - int ix; + const char *pattern = NULL; - for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir); ++ix) - if (*filename_realp == NULL && filename_is_in_dir (filename, dir)) - break; + for (const gdb::unique_xmalloc_ptr &p : auto_load_safe_path_vec) + if (*filename_realp == NULL && filename_is_in_pattern (filename, p.get ())) + { + pattern = p.get (); + break; + } - if (dir == NULL) + if (pattern == NULL) { if (*filename_realp == NULL) { *filename_realp = gdb_realpath (filename); - if (debug_auto_load && strcmp (*filename_realp, filename) != 0) + if (debug_auto_load && strcmp (filename_realp->get (), filename) != 0) fprintf_unfiltered (gdb_stdlog, _("auto-load: Resolved " "file \"%s\" as \"%s\".\n"), - filename, *filename_realp); + filename, filename_realp->get ()); } - if (strcmp (*filename_realp, filename) != 0) - for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir); - ++ix) - if (filename_is_in_dir (*filename_realp, dir)) - break; + if (strcmp (filename_realp->get (), filename) != 0) + for (const gdb::unique_xmalloc_ptr &p : auto_load_safe_path_vec) + if (filename_is_in_pattern (filename_realp->get (), p.get ())) + { + pattern = p.get (); + break; + } } - if (dir != NULL) + if (pattern != NULL) { if (debug_auto_load) fprintf_unfiltered (gdb_stdlog, _("auto-load: File \"%s\" matches " "directory \"%s\".\n"), - filename, dir); + filename, pattern); return 1; } @@ -353,8 +472,8 @@ filename_is_in_auto_load_safe_path_vec (const char *filename, int file_is_auto_load_safe (const char *filename, const char *debug_fmt, ...) { - char *filename_real = NULL; - struct cleanup *back_to; + gdb::unique_xmalloc_ptr filename_real; + static int advice_printed = 0; if (debug_auto_load) { @@ -365,61 +484,42 @@ file_is_auto_load_safe (const char *filename, const char *debug_fmt, ...) va_end (debug_args); } - back_to = make_cleanup (free_current_contents, &filename_real); - if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real)) - { - do_cleanups (back_to); - return 1; - } + return 1; auto_load_safe_path_vec_update (); if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real)) - { - do_cleanups (back_to); - return 1; - } + return 1; - warning (_("File \"%s\" auto-loading has been declined by your " + warning (_("File \"%ps\" auto-loading has been declined by your " "`auto-load safe-path' set to \"%s\"."), - filename_real, auto_load_safe_path); + styled_string (file_name_style.style (), filename_real.get ()), + auto_load_safe_path); - do_cleanups (back_to); - return 0; -} - -/* Definition of script language for GDB canned sequences of commands. */ - -static const struct script_language script_language_gdb - = { GDB_AUTO_FILE_NAME, source_gdb_script_for_objfile }; - -static void -source_gdb_script_for_objfile (struct objfile *objfile, FILE *file, - const char *filename) -{ - int is_safe; - struct auto_load_pspace_info *pspace_info; - volatile struct gdb_exception e; - - is_safe = file_is_auto_load_safe (filename, _("auto-load: Loading canned " - "sequences of commands script " - "\"%s\" for objfile \"%s\".\n"), - filename, objfile->name); - - /* Add this script to the hash table too so "info auto-load gdb-scripts" - can print it. */ - pspace_info = get_auto_load_pspace_data_for_loading (current_program_space); - maybe_add_script (pspace_info, is_safe, filename, filename, - &script_language_gdb); - - if (!is_safe) - return; - - TRY_CATCH (e, RETURN_MASK_ALL) + if (!advice_printed) { - script_from_file (file, filename); + const char *homedir = getenv ("HOME"); + + if (homedir == NULL) + homedir = "$HOME"; + std::string homeinit = string_printf ("%s/%s", homedir, GDBINIT); + + printf_filtered (_("\ +To enable execution of this file add\n\ +\tadd-auto-load-safe-path %s\n\ +line to your configuration file \"%s\".\n\ +To completely disable this security protection add\n\ +\tset auto-load safe-path /\n\ +line to your configuration file \"%s\".\n\ +For more information about this security protection see the\n\ +\"Auto-loading safe path\" section in the GDB manual. E.g., run from the shell:\n\ +\tinfo \"(gdb)Auto-loading safe path\"\n"), + filename_real.get (), + homeinit.c_str (), homeinit.c_str ()); + advice_printed = 1; } - exception_print (gdb_stderr, e); + + return 0; } /* For scripts specified in .debug_gdb_scripts, multiple objfiles may load @@ -429,15 +529,24 @@ source_gdb_script_for_objfile (struct objfile *objfile, FILE *file, struct auto_load_pspace_info { - /* For each program space we keep track of loaded scripts. */ - struct htab *loaded_scripts; + auto_load_pspace_info () = default; + ~auto_load_pspace_info (); + + /* For each program space we keep track of loaded scripts, both when + specified as file names and as scripts to be executed directly. */ + struct htab *loaded_script_files = nullptr; + struct htab *loaded_script_texts = nullptr; + + /* Non-zero if we've issued the warning about an auto-load script not being + supported. We only want to issue this warning once. */ + bool unsupported_script_warning_printed = false; /* Non-zero if we've issued the warning about an auto-load script not being found. We only want to issue this warning once. */ - int script_not_found_warning_printed; + bool script_not_found_warning_printed = false; }; -/* Objects of this type are stored in the loaded script hash table. */ +/* Objects of this type are stored in the loaded_script hash table. */ struct loaded_script { @@ -445,30 +554,25 @@ struct loaded_script const char *name; /* Full path name or NULL if script wasn't found (or was otherwise - inaccessible). */ + inaccessible), or NULL for loaded_script_texts. */ const char *full_path; /* Non-zero if this script has been loaded. */ int loaded; - const struct script_language *language; + const struct extension_language_defn *language; }; /* Per-program-space data key. */ -static const struct program_space_data *auto_load_pspace_data; +static const struct program_space_key + auto_load_pspace_data; -static void -auto_load_pspace_data_cleanup (struct program_space *pspace, void *arg) +auto_load_pspace_info::~auto_load_pspace_info () { - struct auto_load_pspace_info *info; - - info = program_space_data (pspace, auto_load_pspace_data); - if (info != NULL) - { - if (info->loaded_scripts) - htab_delete (info->loaded_scripts); - xfree (info); - } + if (loaded_script_files) + htab_delete (loaded_script_files); + if (loaded_script_texts) + htab_delete (loaded_script_texts); } /* Get the current autoload data. If none is found yet, add it now. This @@ -479,12 +583,9 @@ get_auto_load_pspace_data (struct program_space *pspace) { struct auto_load_pspace_info *info; - info = program_space_data (pspace, auto_load_pspace_data); + info = auto_load_pspace_data.get (pspace); if (info == NULL) - { - info = XZALLOC (struct auto_load_pspace_info); - set_program_space_data (pspace, auto_load_pspace_data, info); - } + info = auto_load_pspace_data.emplace (pspace); return info; } @@ -494,7 +595,7 @@ get_auto_load_pspace_data (struct program_space *pspace) static hashval_t hash_loaded_script_entry (const void *data) { - const struct loaded_script *e = data; + const struct loaded_script *e = (const struct loaded_script *) data; return htab_hash_string (e->name) ^ htab_hash_pointer (e->language); } @@ -504,8 +605,8 @@ hash_loaded_script_entry (const void *data) static int eq_loaded_script_entry (const void *a, const void *b) { - const struct loaded_script *ea = a; - const struct loaded_script *eb = b; + const struct loaded_script *ea = (const struct loaded_script *) a; + const struct loaded_script *eb = (const struct loaded_script *) b; return strcmp (ea->name, eb->name) == 0 && ea->language == eb->language; } @@ -520,12 +621,17 @@ init_loaded_scripts_info (struct auto_load_pspace_info *pspace_info) Space for each entry is obtained with one malloc so we can free them easily. */ - pspace_info->loaded_scripts = htab_create (31, - hash_loaded_script_entry, - eq_loaded_script_entry, - xfree); - - pspace_info->script_not_found_warning_printed = FALSE; + pspace_info->loaded_script_files = htab_create (31, + hash_loaded_script_entry, + eq_loaded_script_entry, + xfree); + pspace_info->loaded_script_texts = htab_create (31, + hash_loaded_script_entry, + eq_loaded_script_entry, + xfree); + + pspace_info->unsupported_script_warning_printed = false; + pspace_info->script_not_found_warning_printed = false; } /* Wrapper on get_auto_load_pspace_data to also allocate the hash table @@ -537,23 +643,24 @@ get_auto_load_pspace_data_for_loading (struct program_space *pspace) struct auto_load_pspace_info *info; info = get_auto_load_pspace_data (pspace); - if (info->loaded_scripts == NULL) + if (info->loaded_script_files == NULL) init_loaded_scripts_info (info); return info; } -/* Add script NAME in LANGUAGE to hash table of PSPACE_INFO. LOADED 1 if the - script has been (is going to) be loaded, 0 otherwise (such as if it has not - been found). FULL_PATH is NULL if the script wasn't found. The result is - true if the script was already in the hash table. */ +/* Add script file NAME in LANGUAGE to hash table of PSPACE_INFO. + LOADED 1 if the script has been (is going to) be loaded, 0 otherwise + (such as if it has not been found). + FULL_PATH is NULL if the script wasn't found. + The result is true if the script was already in the hash table. */ -int -maybe_add_script (struct auto_load_pspace_info *pspace_info, int loaded, - const char *name, const char *full_path, - const struct script_language *language) +static int +maybe_add_script_file (struct auto_load_pspace_info *pspace_info, int loaded, + const char *name, const char *full_path, + const struct extension_language_defn *language) { - struct htab *htab = pspace_info->loaded_scripts; + struct htab *htab = pspace_info->loaded_script_files; struct loaded_script **slot, entry; int in_hash_table; @@ -564,14 +671,15 @@ maybe_add_script (struct auto_load_pspace_info *pspace_info, int loaded, /* If this script is not in the hash table, add it. */ - if (! in_hash_table) + if (!in_hash_table) { char *p; /* Allocate all space in one chunk so it's easier to free. */ - *slot = xmalloc (sizeof (**slot) - + strlen (name) + 1 - + (full_path != NULL ? (strlen (full_path) + 1) : 0)); + *slot = ((struct loaded_script *) + xmalloc (sizeof (**slot) + + strlen (name) + 1 + + (full_path != NULL ? (strlen (full_path) + 1) : 0))); p = ((char*) *slot) + sizeof (**slot); strcpy (p, name); (*slot)->name = p; @@ -590,6 +698,45 @@ maybe_add_script (struct auto_load_pspace_info *pspace_info, int loaded, return in_hash_table; } +/* Add script contents NAME in LANGUAGE to hash table of PSPACE_INFO. + LOADED 1 if the script has been (is going to) be loaded, 0 otherwise + (such as if it has not been found). + The result is true if the script was already in the hash table. */ + +static int +maybe_add_script_text (struct auto_load_pspace_info *pspace_info, + int loaded, const char *name, + const struct extension_language_defn *language) +{ + struct htab *htab = pspace_info->loaded_script_texts; + struct loaded_script **slot, entry; + int in_hash_table; + + entry.name = name; + entry.language = language; + slot = (struct loaded_script **) htab_find_slot (htab, &entry, INSERT); + in_hash_table = *slot != NULL; + + /* If this script is not in the hash table, add it. */ + + if (!in_hash_table) + { + char *p; + + /* Allocate all space in one chunk so it's easier to free. */ + *slot = ((struct loaded_script *) + xmalloc (sizeof (**slot) + strlen (name) + 1)); + p = ((char*) *slot) + sizeof (**slot); + strcpy (p, name); + (*slot)->name = p; + (*slot)->full_path = NULL; + (*slot)->loaded = loaded; + (*slot)->language = language; + } + + return in_hash_table; +} + /* Clear the table of loaded section scripts. */ static void @@ -598,69 +745,52 @@ clear_section_scripts (void) struct program_space *pspace = current_program_space; struct auto_load_pspace_info *info; - info = program_space_data (pspace, auto_load_pspace_data); - if (info != NULL && info->loaded_scripts != NULL) - { - htab_delete (info->loaded_scripts); - info->loaded_scripts = NULL; - info->script_not_found_warning_printed = FALSE; - } + info = auto_load_pspace_data.get (pspace); + if (info != NULL && info->loaded_script_files != NULL) + auto_load_pspace_data.clear (pspace); } -/* Look for the auto-load script in LANGUAGE associated with OBJFILE and load - it. */ +/* Look for the auto-load script in LANGUAGE associated with OBJFILE where + OBJFILE's gdb_realpath is REALNAME and load it. Return 1 if we found any + matching script, return 0 otherwise. */ -void -auto_load_objfile_script (struct objfile *objfile, - const struct script_language *language) +static int +auto_load_objfile_script_1 (struct objfile *objfile, const char *realname, + const struct extension_language_defn *language) { - char *realname; - char *filename, *debugfile; - int len; - FILE *input; - struct cleanup *cleanups; - - realname = gdb_realpath (objfile->name); - len = strlen (realname); - filename = xmalloc (len + strlen (language->suffix) + 1); - memcpy (filename, realname, len); - strcpy (filename + len, language->suffix); - - cleanups = make_cleanup (xfree, filename); - make_cleanup (xfree, realname); - - input = fopen (filename, "r"); - debugfile = filename; + const char *debugfile; + int retval; + const char *suffix = ext_lang_auto_load_suffix (language); + + std::string filename = std::string (realname) + suffix; + + gdb_file_up input = gdb_fopen_cloexec (filename.c_str (), "r"); + debugfile = filename.c_str (); if (debug_auto_load) fprintf_unfiltered (gdb_stdlog, _("auto-load: Attempted file \"%s\" %s.\n"), debugfile, input ? _("exists") : _("does not exist")); + std::string debugfile_holder; if (!input) { - char *debugdir; - VEC (char_ptr) *debugdir_vec; - int ix; + /* Also try the same file in a subdirectory of gdb's data + directory. */ - debugdir_vec = dirnames_to_char_ptr_vec (debug_file_directory); - make_cleanup_free_char_ptr_vec (debugdir_vec); + std::vector> vec + = auto_load_expand_dir_vars (auto_load_dir); if (debug_auto_load) - fprintf_unfiltered (gdb_stdlog, - _("auto-load: Searching 'set debug-file-directory' " - "path \"%s\".\n"), - debug_file_directory); + fprintf_unfiltered (gdb_stdlog, _("auto-load: Searching 'set auto-load " + "scripts-directory' path \"%s\".\n"), + auto_load_dir); - for (ix = 0; VEC_iterate (char_ptr, debugdir_vec, ix, debugdir); ++ix) + for (const gdb::unique_xmalloc_ptr &dir : vec) { - /* Also try the same file in the separate debug info directory. */ - debugfile = xmalloc (strlen (debugdir) + strlen (filename) + 1); - strcpy (debugfile, debugdir); - /* FILENAME is absolute, so we don't need a "/" here. */ - strcat (debugfile, filename); + debugfile_holder = dir.get () + filename; + debugfile = debugfile_holder.c_str (); - make_cleanup (xfree, debugfile); - input = fopen (debugfile, "r"); + input = gdb_fopen_cloexec (debugfile, "r"); if (debug_auto_load) fprintf_unfiltered (gdb_stdlog, _("auto-load: Attempted file " "\"%s\" %s.\n"), @@ -671,58 +801,341 @@ auto_load_objfile_script (struct objfile *objfile, } } - if (!input) + if (input) { - VEC (char_ptr) *vec; - int ix; - char *dir; + int is_safe; + struct auto_load_pspace_info *pspace_info; + + is_safe + = file_is_auto_load_safe (debugfile, + _("auto-load: Loading %s script \"%s\"" + " by extension for objfile \"%s\".\n"), + ext_lang_name (language), + debugfile, objfile_name (objfile)); + + /* Add this script to the hash table too so + "info auto-load ${lang}-scripts" can print it. */ + pspace_info + = get_auto_load_pspace_data_for_loading (current_program_space); + maybe_add_script_file (pspace_info, is_safe, debugfile, debugfile, + language); - /* Also try the same file in a subdirectory of gdb's data - directory. */ + /* To preserve existing behaviour we don't check for whether the + script was already in the table, and always load it. + It's highly unlikely that we'd ever load it twice, + and these scripts are required to be idempotent under multiple + loads anyway. */ + if (is_safe) + { + objfile_script_sourcer_func *sourcer + = ext_lang_objfile_script_sourcer (language); + + /* We shouldn't get here if support for the language isn't + compiled in. And the extension language is required to implement + this function. */ + gdb_assert (sourcer != NULL); + sourcer (language, objfile, input.get (), debugfile); + } - vec = dirnames_to_char_ptr_vec (auto_load_dir); - make_cleanup_free_char_ptr_vec (vec); + retval = 1; + } + else + retval = 0; - if (debug_auto_load) - fprintf_unfiltered (gdb_stdlog, _("auto-load: Searching 'set auto-load " - "scripts-directory' path \"%s\".\n"), - auto_load_dir); + return retval; +} - for (ix = 0; VEC_iterate (char_ptr, vec, ix, dir); ++ix) - { - debugfile = xstrdup (dir); - substitute_path_component (&debugfile, "$ddir", gdb_datadir); - debugfile = xrealloc (debugfile, (strlen (debugfile) - + strlen (filename) + 1)); +/* Look for the auto-load script in LANGUAGE associated with OBJFILE and load + it. */ - /* FILENAME is absolute, so we don't need a "/" here. */ - strcat (debugfile, filename); +void +auto_load_objfile_script (struct objfile *objfile, + const struct extension_language_defn *language) +{ + gdb::unique_xmalloc_ptr realname + = gdb_realpath (objfile_name (objfile)); + + if (!auto_load_objfile_script_1 (objfile, realname.get (), language)) + { + /* For Windows/DOS .exe executables, strip the .exe suffix, so that + FOO-gdb.gdb could be used for FOO.exe, and try again. */ + + size_t len = strlen (realname.get ()); + const size_t lexe = sizeof (".exe") - 1; - make_cleanup (xfree, debugfile); - input = fopen (debugfile, "r"); + if (len > lexe && strcasecmp (realname.get () + len - lexe, ".exe") == 0) + { + len -= lexe; + realname.get ()[len] = '\0'; if (debug_auto_load) - fprintf_unfiltered (gdb_stdlog, _("auto-load: Attempted file " - "\"%s\" %s.\n"), - debugfile, - input ? _("exists") : _("does not exist")); - if (input != NULL) + fprintf_unfiltered (gdb_stdlog, _("auto-load: Stripped .exe suffix, " + "retrying with \"%s\".\n"), + realname.get ()); + auto_load_objfile_script_1 (objfile, realname.get (), language); + } + } +} + +/* Subroutine of source_section_scripts to simplify it. + Load FILE as a script in extension language LANGUAGE. + The script is from section SECTION_NAME in OBJFILE at offset OFFSET. */ + +static void +source_script_file (struct auto_load_pspace_info *pspace_info, + struct objfile *objfile, + const struct extension_language_defn *language, + const char *section_name, unsigned int offset, + const char *file) +{ + int in_hash_table; + objfile_script_sourcer_func *sourcer; + + /* Skip this script if support is not compiled in. */ + sourcer = ext_lang_objfile_script_sourcer (language); + if (sourcer == NULL) + { + /* We don't throw an error, the program is still debuggable. */ + maybe_print_unsupported_script_warning (pspace_info, objfile, language, + section_name, offset); + /* We *could* still try to open it, but there's no point. */ + maybe_add_script_file (pspace_info, 0, file, NULL, language); + return; + } + + /* Skip this script if auto-loading it has been disabled. */ + if (!ext_lang_auto_load_enabled (language)) + { + /* No message is printed, just skip it. */ + return; + } + + gdb::optional opened = find_and_open_script (file, + 1 /*search_path*/); + + if (opened) + { + if (!file_is_auto_load_safe (opened->full_path.get (), + _("auto-load: Loading %s script " + "\"%s\" from section \"%s\" of " + "objfile \"%s\".\n"), + ext_lang_name (language), + opened->full_path.get (), + section_name, objfile_name (objfile))) + opened.reset (); + } + else + { + /* If one script isn't found it's not uncommon for more to not be + found either. We don't want to print a message for each script, + too much noise. Instead, we print the warning once and tell the + user how to find the list of scripts that weren't loaded. + We don't throw an error, the program is still debuggable. + + IWBN if complaints.c were more general-purpose. */ + + maybe_print_script_not_found_warning (pspace_info, objfile, language, + section_name, offset); + } + + in_hash_table = maybe_add_script_file (pspace_info, bool (opened), file, + (opened + ? opened->full_path.get () + : NULL), + language); + + /* If this file is not currently loaded, load it. */ + if (opened && !in_hash_table) + sourcer (language, objfile, opened->stream.get (), + opened->full_path.get ()); +} + +/* Subroutine of source_section_scripts to simplify it. + Execute SCRIPT as a script in extension language LANG. + The script is from section SECTION_NAME in OBJFILE at offset OFFSET. */ + +static void +execute_script_contents (struct auto_load_pspace_info *pspace_info, + struct objfile *objfile, + const struct extension_language_defn *language, + const char *section_name, unsigned int offset, + const char *script) +{ + objfile_script_executor_func *executor; + const char *newline, *script_text; + const char *name; + int is_safe, in_hash_table; + + /* The first line of the script is the name of the script. + It must not contain any kind of space character. */ + name = NULL; + newline = strchr (script, '\n'); + std::string name_holder; + if (newline != NULL) + { + const char *buf, *p; + + /* Put the name in a buffer and validate it. */ + name_holder = std::string (script, newline - script); + buf = name_holder.c_str (); + for (p = buf; *p != '\0'; ++p) + { + if (isspace (*p)) break; } + /* We don't allow nameless scripts, they're not helpful to the user. */ + if (p != buf && *p == '\0') + name = buf; } + if (name == NULL) + { + /* We don't throw an error, the program is still debuggable. */ + warning (_("\ +Missing/bad script name in entry at offset %u in section %s\n\ +of file %ps."), + offset, section_name, + styled_string (file_name_style.style (), + objfile_name (objfile))); + return; + } + script_text = newline + 1; - if (input) + /* Skip this script if support is not compiled in. */ + executor = ext_lang_objfile_script_executor (language); + if (executor == NULL) { - make_cleanup_fclose (input); + /* We don't throw an error, the program is still debuggable. */ + maybe_print_unsupported_script_warning (pspace_info, objfile, language, + section_name, offset); + maybe_add_script_text (pspace_info, 0, name, language); + return; + } - /* To preserve existing behaviour we don't check for whether the - script was already in the table, and always load it. - It's highly unlikely that we'd ever load it twice, - and these scripts are required to be idempotent under multiple - loads anyway. */ - language->source_script_for_objfile (objfile, input, debugfile); + /* Skip this script if auto-loading it has been disabled. */ + if (!ext_lang_auto_load_enabled (language)) + { + /* No message is printed, just skip it. */ + return; + } + + is_safe = file_is_auto_load_safe (objfile_name (objfile), + _("auto-load: Loading %s script " + "\"%s\" from section \"%s\" of " + "objfile \"%s\".\n"), + ext_lang_name (language), name, + section_name, objfile_name (objfile)); + + in_hash_table = maybe_add_script_text (pspace_info, is_safe, name, language); + + /* If this file is not currently loaded, load it. */ + if (is_safe && !in_hash_table) + executor (language, objfile, name, script_text); +} + +/* Load scripts specified in OBJFILE. + START,END delimit a buffer containing a list of nul-terminated + file names. + SECTION_NAME is used in error messages. + + Scripts specified as file names are found per normal "source -s" command + processing. First the script is looked for in $cwd. If not found there + the source search path is used. + + The section contains a list of path names of script files to load or + actual script contents. Each entry is nul-terminated. */ + +static void +source_section_scripts (struct objfile *objfile, const char *section_name, + const char *start, const char *end) +{ + const char *p; + struct auto_load_pspace_info *pspace_info; + + pspace_info = get_auto_load_pspace_data_for_loading (current_program_space); + + for (p = start; p < end; ++p) + { + const char *entry; + const struct extension_language_defn *language; + unsigned int offset = p - start; + int code = *p; + + switch (code) + { + case SECTION_SCRIPT_ID_PYTHON_FILE: + case SECTION_SCRIPT_ID_PYTHON_TEXT: + language = get_ext_lang_defn (EXT_LANG_PYTHON); + break; + case SECTION_SCRIPT_ID_SCHEME_FILE: + case SECTION_SCRIPT_ID_SCHEME_TEXT: + language = get_ext_lang_defn (EXT_LANG_GUILE); + break; + default: + warning (_("Invalid entry in %s section"), section_name); + /* We could try various heuristics to find the next valid entry, + but it's safer to just punt. */ + return; + } + entry = ++p; + + while (p < end && *p != '\0') + ++p; + if (p == end) + { + warning (_("Non-nul-terminated entry in %s at offset %u"), + section_name, offset); + /* Don't load/execute it. */ + break; + } + + switch (code) + { + case SECTION_SCRIPT_ID_PYTHON_FILE: + case SECTION_SCRIPT_ID_SCHEME_FILE: + if (p == entry) + { + warning (_("Empty entry in %s at offset %u"), + section_name, offset); + continue; + } + source_script_file (pspace_info, objfile, language, + section_name, offset, entry); + break; + case SECTION_SCRIPT_ID_PYTHON_TEXT: + case SECTION_SCRIPT_ID_SCHEME_TEXT: + execute_script_contents (pspace_info, objfile, language, + section_name, offset, entry); + break; + } } +} - do_cleanups (cleanups); +/* Load scripts specified in section SECTION_NAME of OBJFILE. */ + +static void +auto_load_section_scripts (struct objfile *objfile, const char *section_name) +{ + bfd *abfd = objfile->obfd; + asection *scripts_sect; + bfd_byte *data = NULL; + + scripts_sect = bfd_get_section_by_name (abfd, section_name); + if (scripts_sect == NULL + || (bfd_section_flags (scripts_sect) & SEC_HAS_CONTENTS) == 0) + return; + + if (!bfd_get_full_section_contents (abfd, scripts_sect, &data)) + warning (_("Couldn't read %s section of %ps"), + section_name, + styled_string (file_name_style.style (), + bfd_get_filename (abfd))); + else + { + gdb::unique_xmalloc_ptr data_holder (data); + + char *p = (char *) data; + source_section_scripts (objfile, section_name, p, + p + bfd_section_size (scripts_sect)); + } } /* Load any auto-loaded scripts for OBJFILE. */ @@ -730,13 +1143,21 @@ auto_load_objfile_script (struct objfile *objfile, void load_auto_scripts_for_objfile (struct objfile *objfile) { - if (!global_auto_load) + /* Return immediately if auto-loading has been globally disabled. + This is to handle sequencing of operations during gdb startup. + Also return immediately if OBJFILE was not created from a file + on the local filesystem. */ + if (!global_auto_load + || (objfile->flags & OBJF_NOT_FILENAME) != 0 + || is_target_filename (objfile->original_name)) return; - if (auto_load_gdb_scripts) - auto_load_objfile_script (objfile, &script_language_gdb); + /* Load any extension language scripts for this objfile. + E.g., foo-gdb.gdb, foo-gdb.py. */ + auto_load_ext_lang_scripts_for_objfile (objfile); - gdbpy_load_auto_scripts_for_objfile (objfile); + /* Load any scripts mentioned in AUTO_SECTION_NAME (.debug_gdb_scripts). */ + auto_load_section_scripts (objfile, AUTO_SECTION_NAME); } /* This is a new_objfile observer callback to auto-load scripts. @@ -760,14 +1181,15 @@ auto_load_new_objfile (struct objfile *objfile) /* Collect scripts to be printed in a vec. */ -typedef struct loaded_script *loaded_script_ptr; -DEF_VEC_P (loaded_script_ptr); - struct collect_matching_scripts_data { - VEC (loaded_script_ptr) **scripts_p; + collect_matching_scripts_data (std::vector *scripts_p_, + const extension_language_defn *language_) + : scripts_p (scripts_p_), language (language_) + {} - const struct script_language *language; + std::vector *scripts_p; + const struct extension_language_defn *language; }; /* Traversal function for htab_traverse. @@ -776,11 +1198,12 @@ struct collect_matching_scripts_data static int collect_matching_scripts (void **slot, void *info) { - struct loaded_script *script = *slot; - struct collect_matching_scripts_data *data = info; + struct loaded_script *script = (struct loaded_script *) *slot; + struct collect_matching_scripts_data *data + = (struct collect_matching_scripts_data *) info; if (script->language == data->language && re_exec (script->name)) - VEC_safe_push (loaded_script_ptr, *data->scripts_p, script); + data->scripts_p->push_back (script); return 1; } @@ -791,35 +1214,29 @@ static void print_script (struct loaded_script *script) { struct ui_out *uiout = current_uiout; - struct cleanup *chain; - chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + ui_out_emit_tuple tuple_emitter (uiout, NULL); - ui_out_field_string (uiout, "loaded", script->loaded ? "Yes" : "No"); - ui_out_field_string (uiout, "script", script->name); - ui_out_text (uiout, "\n"); + uiout->field_string ("loaded", script->loaded ? "Yes" : "No"); + uiout->field_string ("script", script->name); + uiout->text ("\n"); /* If the name isn't the full path, print it too. */ if (script->full_path != NULL && strcmp (script->name, script->full_path) != 0) { - ui_out_text (uiout, "\tfull name: "); - ui_out_field_string (uiout, "full_path", script->full_path); - ui_out_text (uiout, "\n"); + uiout->text ("\tfull name: "); + uiout->field_string ("full_path", script->full_path); + uiout->text ("\n"); } - - do_cleanups (chain); } /* Helper for info_auto_load_scripts to sort the scripts by name. */ -static int -sort_scripts_by_name (const void *ap, const void *bp) +static bool +sort_scripts_by_name (loaded_script *a, loaded_script *b) { - const struct loaded_script *a = *(const struct loaded_script **) ap; - const struct loaded_script *b = *(const struct loaded_script **) bp; - - return FILENAME_CMP (a->name, b->name); + return FILENAME_CMP (a->name, b->name) < 0; } /* Special internal GDB value of auto_load_info_scripts's PATTERN identify @@ -827,19 +1244,26 @@ sort_scripts_by_name (const void *ap, const void *bp) "info auto-load" invocation. Extra newline will be printed if needed. */ char auto_load_info_scripts_pattern_nl[] = ""; +/* Subroutine of auto_load_info_scripts to simplify it. + Print SCRIPTS. */ + +static void +print_scripts (const std::vector &scripts) +{ + for (loaded_script *script : scripts) + print_script (script); +} + /* Implementation for "info auto-load gdb-scripts" (and "info auto-load python-scripts"). List scripts in LANGUAGE matching PATTERN. FROM_TTY is the usual GDB boolean for user interactivity. */ void -auto_load_info_scripts (char *pattern, int from_tty, - const struct script_language *language) +auto_load_info_scripts (const char *pattern, int from_tty, + const struct extension_language_defn *language) { struct ui_out *uiout = current_uiout; struct auto_load_pspace_info *pspace_info; - struct cleanup *script_chain; - VEC (loaded_script_ptr) *scripts; - int nr_scripts; dont_repeat (); @@ -861,99 +1285,137 @@ auto_load_info_scripts (char *pattern, int from_tty, Plus we want to sort the scripts by name. So first traverse the hash table collecting the matching scripts. */ - scripts = VEC_alloc (loaded_script_ptr, 10); - script_chain = make_cleanup (VEC_cleanup (loaded_script_ptr), &scripts); + std::vector script_files, script_texts; - if (pspace_info != NULL && pspace_info->loaded_scripts != NULL) + if (pspace_info != NULL && pspace_info->loaded_script_files != NULL) { - struct collect_matching_scripts_data data = { &scripts, language }; + collect_matching_scripts_data data (&script_files, language); - immediate_quit++; /* Pass a pointer to scripts as VEC_safe_push can realloc space. */ - htab_traverse_noresize (pspace_info->loaded_scripts, + htab_traverse_noresize (pspace_info->loaded_script_files, collect_matching_scripts, &data); - immediate_quit--; + + std::sort (script_files.begin (), script_files.end (), + sort_scripts_by_name); + } + + if (pspace_info != NULL && pspace_info->loaded_script_texts != NULL) + { + collect_matching_scripts_data data (&script_texts, language); + + /* Pass a pointer to scripts as VEC_safe_push can realloc space. */ + htab_traverse_noresize (pspace_info->loaded_script_texts, + collect_matching_scripts, &data); + + std::sort (script_texts.begin (), script_texts.end (), + sort_scripts_by_name); } - nr_scripts = VEC_length (loaded_script_ptr, scripts); + int nr_scripts = script_files.size () + script_texts.size (); /* Table header shifted right by preceding "gdb-scripts: " would not match its columns. */ if (nr_scripts > 0 && pattern == auto_load_info_scripts_pattern_nl) - ui_out_text (uiout, "\n"); + uiout->text ("\n"); - make_cleanup_ui_out_table_begin_end (uiout, 2, nr_scripts, - "AutoLoadedScriptsTable"); + { + ui_out_emit_table table_emitter (uiout, 2, nr_scripts, + "AutoLoadedScriptsTable"); - ui_out_table_header (uiout, 7, ui_left, "loaded", "Loaded"); - ui_out_table_header (uiout, 70, ui_left, "script", "Script"); - ui_out_table_body (uiout); - - if (nr_scripts > 0) - { - int i; - loaded_script_ptr script; - - qsort (VEC_address (loaded_script_ptr, scripts), - VEC_length (loaded_script_ptr, scripts), - sizeof (loaded_script_ptr), sort_scripts_by_name); - for (i = 0; VEC_iterate (loaded_script_ptr, scripts, i, script); ++i) - print_script (script); - } + uiout->table_header (7, ui_left, "loaded", "Loaded"); + uiout->table_header (70, ui_left, "script", "Script"); + uiout->table_body (); - do_cleanups (script_chain); + print_scripts (script_files); + print_scripts (script_texts); + } if (nr_scripts == 0) { if (pattern && *pattern) - ui_out_message (uiout, 0, "No auto-load scripts matching %s.\n", - pattern); + uiout->message ("No auto-load scripts matching %s.\n", pattern); else - ui_out_message (uiout, 0, "No auto-load scripts.\n"); + uiout->message ("No auto-load scripts.\n"); } } /* Wrapper for "info auto-load gdb-scripts". */ static void -info_auto_load_gdb_scripts (char *pattern, int from_tty) +info_auto_load_gdb_scripts (const char *pattern, int from_tty) { - auto_load_info_scripts (pattern, from_tty, &script_language_gdb); + auto_load_info_scripts (pattern, from_tty, &extension_language_gdb); } /* Implement 'info auto-load local-gdbinit'. */ static void -info_auto_load_local_gdbinit (char *args, int from_tty) +info_auto_load_local_gdbinit (const char *args, int from_tty) { if (auto_load_local_gdbinit_pathname == NULL) printf_filtered (_("Local .gdbinit file was not found.\n")); else if (auto_load_local_gdbinit_loaded) - printf_filtered (_("Local .gdbinit file \"%s\" has been loaded.\n"), - auto_load_local_gdbinit_pathname); + printf_filtered (_("Local .gdbinit file \"%ps\" has been loaded.\n"), + styled_string (file_name_style.style (), + auto_load_local_gdbinit_pathname)); else - printf_filtered (_("Local .gdbinit file \"%s\" has not been loaded.\n"), - auto_load_local_gdbinit_pathname); + printf_filtered (_("Local .gdbinit file \"%ps\" has not been loaded.\n"), + styled_string (file_name_style.style (), + auto_load_local_gdbinit_pathname)); +} + +/* Print an "unsupported script" warning if it has not already been printed. + The script is in language LANGUAGE at offset OFFSET in section SECTION_NAME + of OBJFILE. */ + +static void +maybe_print_unsupported_script_warning + (struct auto_load_pspace_info *pspace_info, + struct objfile *objfile, const struct extension_language_defn *language, + const char *section_name, unsigned offset) +{ + if (!pspace_info->unsupported_script_warning_printed) + { + warning (_("\ +Unsupported auto-load script at offset %u in section %s\n\ +of file %ps.\n\ +Use `info auto-load %s-scripts [REGEXP]' to list them."), + offset, section_name, + styled_string (file_name_style.style (), + objfile_name (objfile)), + ext_lang_name (language)); + pspace_info->unsupported_script_warning_printed = true; + } } /* Return non-zero if SCRIPT_NOT_FOUND_WARNING_PRINTED of PSPACE_INFO was unset before calling this function. Always set SCRIPT_NOT_FOUND_WARNING_PRINTED of PSPACE_INFO. */ -int -script_not_found_warning_print (struct auto_load_pspace_info *pspace_info) +static void +maybe_print_script_not_found_warning + (struct auto_load_pspace_info *pspace_info, + struct objfile *objfile, const struct extension_language_defn *language, + const char *section_name, unsigned offset) { - int retval = !pspace_info->script_not_found_warning_printed; - - pspace_info->script_not_found_warning_printed = 1; - - return retval; + if (!pspace_info->script_not_found_warning_printed) + { + warning (_("\ +Missing auto-load script at offset %u in section %s\n\ +of file %ps.\n\ +Use `info auto-load %s-scripts [REGEXP]' to list them."), + offset, section_name, + styled_string (file_name_style.style (), + objfile_name (objfile)), + ext_lang_name (language)); + pspace_info->script_not_found_warning_printed = true; + } } /* The only valid "set auto-load" argument is off|0|no|disable. */ static void -set_auto_load_cmd (char *args, int from_tty) +set_auto_load_cmd (const char *args, int from_tty) { struct cmd_list_element *list; size_t length; @@ -976,7 +1438,7 @@ set_auto_load_cmd (char *args, int from_tty) if (list->var_type == var_boolean) { gdb_assert (list->type == set_cmd); - do_setshow_command (args, from_tty, list); + do_set_command (args, from_tty, list); } } @@ -998,15 +1460,6 @@ automatic loading of Python scripts."), return &retval; } -/* Command "show auto-load" displays summary of all the current - "show auto-load " settings. */ - -static void -show_auto_load_cmd (char *args, int from_tty) -{ - cmd_show_list (*auto_load_show_cmdlist_get (), from_tty, ""); -} - /* Initialize "show auto-load " commands prefix and return it. */ struct cmd_list_element ** @@ -1015,12 +1468,12 @@ auto_load_show_cmdlist_get (void) static struct cmd_list_element *retval; if (retval == NULL) - add_prefix_cmd ("auto-load", class_maintenance, show_auto_load_cmd, _("\ + add_show_prefix_cmd ("auto-load", class_maintenance, _("\ Show auto-loading specific settings.\n\ Show configuration of various auto-load-specific variables such as\n\ automatic loading of Python scripts."), - &retval, "show auto-load ", - 0/*allow-unknown*/, &showlist); + &retval, "show auto-load ", + 0/*allow-unknown*/, &showlist); return &retval; } @@ -1030,32 +1483,24 @@ automatic loading of Python scripts."), newlines at proper places. */ static void -info_auto_load_cmd (char *args, int from_tty) +info_auto_load_cmd (const char *args, int from_tty) { struct cmd_list_element *list; - struct cleanup *infolist_chain; struct ui_out *uiout = current_uiout; - infolist_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "infolist"); + ui_out_emit_tuple tuple_emitter (uiout, "infolist"); for (list = *auto_load_info_cmdlist_get (); list != NULL; list = list->next) { - struct cleanup *option_chain - = make_cleanup_ui_out_tuple_begin_end (uiout, "option"); + ui_out_emit_tuple option_emitter (uiout, "option"); gdb_assert (!list->prefixlist); gdb_assert (list->type == not_set_cmd); - ui_out_field_string (uiout, "name", list->name); - ui_out_text (uiout, ": "); + uiout->field_string ("name", list->name); + uiout->text (": "); cmd_func (list, auto_load_info_scripts_pattern_nl, from_tty); - - /* Close the tuple. */ - do_cleanups (option_chain); } - - /* Close the tuple. */ - do_cleanups (infolist_chain); } /* Initialize "info auto-load " commands prefix and return it. */ @@ -1076,17 +1521,16 @@ found and/or loaded."), return &retval; } -void _initialize_auto_load (void); - +void _initialize_auto_load (); void -_initialize_auto_load (void) +_initialize_auto_load () { struct cmd_list_element *cmd; + char *scripts_directory_help, *gdb_name_help, *python_name_help; + char *guile_name_help; + const char *suffix; - auto_load_pspace_data - = register_program_space_data_with_cleanup (auto_load_pspace_data_cleanup); - - observer_attach_new_objfile (auto_load_new_objfile); + gdb::observers::new_objfile.attach (auto_load_new_objfile); add_setshow_boolean_cmd ("gdb-scripts", class_support, &auto_load_gdb_scripts, _("\ @@ -1095,7 +1539,7 @@ Show whether auto-loading of canned sequences of commands scripts is enabled."), _("\ If enabled, canned sequences of commands are loaded when the debugger reads\n\ an executable or shared library.\n\ -This options has security implications for untrusted inferiors."), +This option has security implications for untrusted inferiors."), NULL, show_auto_load_gdb_scripts, auto_load_set_cmdlist_get (), auto_load_show_cmdlist_get ()); @@ -1113,7 +1557,7 @@ Show whether auto-loading .gdbinit script in current directory is enabled."), If enabled, canned sequences of commands are loaded when debugger starts\n\ from .gdbinit file in current directory. Such files are deprecated,\n\ use a script associated with inferior executable file instead.\n\ -This options has security implications for untrusted inferiors."), +This option has security implications for untrusted inferiors."), NULL, show_auto_load_local_gdbinit, auto_load_set_cmdlist_get (), auto_load_show_cmdlist_get ()); @@ -1124,37 +1568,79 @@ Usage: info auto-load local-gdbinit"), auto_load_info_cmdlist_get ()); auto_load_dir = xstrdup (AUTO_LOAD_DIR); + + suffix = ext_lang_auto_load_suffix (get_ext_lang_defn (EXT_LANG_GDB)); + gdb_name_help + = xstrprintf (_("\ +GDB scripts: OBJFILE%s\n"), + suffix); + python_name_help = NULL; +#ifdef HAVE_PYTHON + suffix = ext_lang_auto_load_suffix (get_ext_lang_defn (EXT_LANG_PYTHON)); + python_name_help + = xstrprintf (_("\ +Python scripts: OBJFILE%s\n"), + suffix); +#endif + guile_name_help = NULL; +#ifdef HAVE_GUILE + suffix = ext_lang_auto_load_suffix (get_ext_lang_defn (EXT_LANG_GUILE)); + guile_name_help + = xstrprintf (_("\ +Guile scripts: OBJFILE%s\n"), + suffix); +#endif + scripts_directory_help + = xstrprintf (_("\ +Automatically loaded scripts are located in one of the directories listed\n\ +by this option.\n\ +\n\ +Script names:\n\ +%s%s%s\ +\n\ +This option is ignored for the kinds of scripts \ +having 'set auto-load ... off'.\n\ +Directories listed here need to be present also \ +in the 'set auto-load safe-path'\n\ +option."), + gdb_name_help, + python_name_help ? python_name_help : "", + guile_name_help ? guile_name_help : ""); + add_setshow_optional_filename_cmd ("scripts-directory", class_support, &auto_load_dir, _("\ Set the list of directories from which to load auto-loaded scripts."), _("\ -Show the list of directories from which to load auto-loaded scripts."), _("\ -Automatically loaded Python scripts and GDB scripts are located in one of the\n\ -directories listed by this option. This option is ignored for the kinds of\n\ -scripts having 'set auto-load ... off'. Directories listed here need to be\n\ -present also in the 'set auto-load safe-path' option."), +Show the list of directories from which to load auto-loaded scripts."), + scripts_directory_help, set_auto_load_dir, show_auto_load_dir, auto_load_set_cmdlist_get (), auto_load_show_cmdlist_get ()); + xfree (scripts_directory_help); + xfree (python_name_help); + xfree (gdb_name_help); + xfree (guile_name_help); auto_load_safe_path = xstrdup (AUTO_LOAD_SAFE_PATH); auto_load_safe_path_vec_update (); add_setshow_optional_filename_cmd ("safe-path", class_support, &auto_load_safe_path, _("\ -Set the list of directories from which it is safe to auto-load files."), _("\ -Show the list of directories from which it is safe to auto-load files."), _("\ +Set the list of files and directories that are safe for auto-loading."), _("\ +Show the list of files and directories that are safe for auto-loading."), _("\ Various files loaded automatically for the 'set auto-load ...' options must\n\ be located in one of the directories listed by this option. Warning will be\n\ printed and file will not be used otherwise.\n\ +You can mix both directory and filename entries.\n\ Setting this parameter to an empty list resets it to its default value.\n\ Setting this parameter to '/' (without the quotes) allows any file\n\ -for the 'set auto-load ...' options.\n\ +for the 'set auto-load ...' options. Each path entry can be also shell\n\ +wildcard pattern; '*' does not match directory separator.\n\ This option is ignored for the kinds of files having 'set auto-load ... off'.\n\ -This options has security implications for untrusted inferiors."), +This option has security implications for untrusted inferiors."), set_auto_load_safe_path, show_auto_load_safe_path, auto_load_set_cmdlist_get (), auto_load_show_cmdlist_get ()); - observer_attach_gdb_datadir_changed (auto_load_gdb_datadir_changed); + gdb::observers::gdb_datadir_changed.attach (auto_load_gdb_datadir_changed); cmd = add_cmd ("add-auto-load-safe-path", class_support, add_auto_load_safe_path, @@ -1165,6 +1651,15 @@ access the current full list setting."), &cmdlist); set_cmd_completer (cmd, filename_completer); + cmd = add_cmd ("add-auto-load-scripts-directory", class_support, + add_auto_load_dir, + _("Add entries to the list of directories from which to load " + "auto-loaded scripts.\n\ +See the commands 'set auto-load scripts-directory' and\n\ +'show auto-load scripts-directory' to access the current full list setting."), + &cmdlist); + set_cmd_completer (cmd, filename_completer); + add_setshow_boolean_cmd ("auto-load", class_maintenance, &debug_auto_load, _("\ Set auto-load verifications debugging."), _("\