4033 lines
124 KiB
C
4033 lines
124 KiB
C
|
/* Generic symbol file reading for the GNU debugger, GDB.
|
|||
|
|
|||
|
Copyright (C) 1990-2020 Free Software Foundation, Inc.
|
|||
|
|
|||
|
Contributed by Cygnus Support, using pieces from other GDB modules.
|
|||
|
|
|||
|
This file is part of GDB.
|
|||
|
|
|||
|
This program is free software; you can redistribute it and/or modify
|
|||
|
it under the terms of the GNU General Public License as published by
|
|||
|
the Free Software Foundation; either version 3 of the License, or
|
|||
|
(at your option) any later version.
|
|||
|
|
|||
|
This program is distributed in the hope that it will be useful,
|
|||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
GNU General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|||
|
|
|||
|
#include "defs.h"
|
|||
|
#include "arch-utils.h"
|
|||
|
#include "bfdlink.h"
|
|||
|
#include "symtab.h"
|
|||
|
#include "gdbtypes.h"
|
|||
|
#include "gdbcore.h"
|
|||
|
#include "frame.h"
|
|||
|
#include "target.h"
|
|||
|
#include "value.h"
|
|||
|
#include "symfile.h"
|
|||
|
#include "objfiles.h"
|
|||
|
#include "source.h"
|
|||
|
#include "gdbcmd.h"
|
|||
|
#include "breakpoint.h"
|
|||
|
#include "language.h"
|
|||
|
#include "complaints.h"
|
|||
|
#include "demangle.h"
|
|||
|
#include "inferior.h"
|
|||
|
#include "regcache.h"
|
|||
|
#include "filenames.h" /* for DOSish file names */
|
|||
|
#include "gdb-stabs.h"
|
|||
|
#include "gdb_obstack.h"
|
|||
|
#include "completer.h"
|
|||
|
#include "bcache.h"
|
|||
|
#include "hashtab.h"
|
|||
|
#include "readline/tilde.h"
|
|||
|
#include "block.h"
|
|||
|
#include "observable.h"
|
|||
|
#include "exec.h"
|
|||
|
#include "parser-defs.h"
|
|||
|
#include "varobj.h"
|
|||
|
#include "elf-bfd.h"
|
|||
|
#include "solib.h"
|
|||
|
#include "remote.h"
|
|||
|
#include "stack.h"
|
|||
|
#include "gdb_bfd.h"
|
|||
|
#include "cli/cli-utils.h"
|
|||
|
#include "gdbsupport/byte-vector.h"
|
|||
|
#include "gdbsupport/pathstuff.h"
|
|||
|
#include "gdbsupport/selftest.h"
|
|||
|
#include "cli/cli-style.h"
|
|||
|
#include "gdbsupport/forward-scope-exit.h"
|
|||
|
|
|||
|
#include <sys/types.h>
|
|||
|
#include <fcntl.h>
|
|||
|
#include <sys/stat.h>
|
|||
|
#include <ctype.h>
|
|||
|
#include <chrono>
|
|||
|
#include <algorithm>
|
|||
|
|
|||
|
#include "psymtab.h"
|
|||
|
|
|||
|
int (*deprecated_ui_load_progress_hook) (const char *section,
|
|||
|
unsigned long num);
|
|||
|
void (*deprecated_show_load_progress) (const char *section,
|
|||
|
unsigned long section_sent,
|
|||
|
unsigned long section_size,
|
|||
|
unsigned long total_sent,
|
|||
|
unsigned long total_size);
|
|||
|
void (*deprecated_pre_add_symbol_hook) (const char *);
|
|||
|
void (*deprecated_post_add_symbol_hook) (void);
|
|||
|
|
|||
|
using clear_symtab_users_cleanup
|
|||
|
= FORWARD_SCOPE_EXIT (clear_symtab_users);
|
|||
|
|
|||
|
/* Global variables owned by this file. */
|
|||
|
int readnow_symbol_files; /* Read full symbols immediately. */
|
|||
|
int readnever_symbol_files; /* Never read full symbols. */
|
|||
|
|
|||
|
/* Functions this file defines. */
|
|||
|
|
|||
|
static void symbol_file_add_main_1 (const char *args, symfile_add_flags add_flags,
|
|||
|
objfile_flags flags, CORE_ADDR reloff);
|
|||
|
|
|||
|
static const struct sym_fns *find_sym_fns (bfd *);
|
|||
|
|
|||
|
static void overlay_invalidate_all (void);
|
|||
|
|
|||
|
static void simple_free_overlay_table (void);
|
|||
|
|
|||
|
static void read_target_long_array (CORE_ADDR, unsigned int *, int, int,
|
|||
|
enum bfd_endian);
|
|||
|
|
|||
|
static int simple_read_overlay_table (void);
|
|||
|
|
|||
|
static int simple_overlay_update_1 (struct obj_section *);
|
|||
|
|
|||
|
static void symfile_find_segment_sections (struct objfile *objfile);
|
|||
|
|
|||
|
/* List of all available sym_fns. On gdb startup, each object file reader
|
|||
|
calls add_symtab_fns() to register information on each format it is
|
|||
|
prepared to read. */
|
|||
|
|
|||
|
struct registered_sym_fns
|
|||
|
{
|
|||
|
registered_sym_fns (bfd_flavour sym_flavour_, const struct sym_fns *sym_fns_)
|
|||
|
: sym_flavour (sym_flavour_), sym_fns (sym_fns_)
|
|||
|
{}
|
|||
|
|
|||
|
/* BFD flavour that we handle. */
|
|||
|
enum bfd_flavour sym_flavour;
|
|||
|
|
|||
|
/* The "vtable" of symbol functions. */
|
|||
|
const struct sym_fns *sym_fns;
|
|||
|
};
|
|||
|
|
|||
|
static std::vector<registered_sym_fns> symtab_fns;
|
|||
|
|
|||
|
/* Values for "set print symbol-loading". */
|
|||
|
|
|||
|
const char print_symbol_loading_off[] = "off";
|
|||
|
const char print_symbol_loading_brief[] = "brief";
|
|||
|
const char print_symbol_loading_full[] = "full";
|
|||
|
static const char *print_symbol_loading_enums[] =
|
|||
|
{
|
|||
|
print_symbol_loading_off,
|
|||
|
print_symbol_loading_brief,
|
|||
|
print_symbol_loading_full,
|
|||
|
NULL
|
|||
|
};
|
|||
|
static const char *print_symbol_loading = print_symbol_loading_full;
|
|||
|
|
|||
|
/* See symfile.h. */
|
|||
|
|
|||
|
bool auto_solib_add = true;
|
|||
|
|
|||
|
|
|||
|
/* Return non-zero if symbol-loading messages should be printed.
|
|||
|
FROM_TTY is the standard from_tty argument to gdb commands.
|
|||
|
If EXEC is non-zero the messages are for the executable.
|
|||
|
Otherwise, messages are for shared libraries.
|
|||
|
If FULL is non-zero then the caller is printing a detailed message.
|
|||
|
E.g., the message includes the shared library name.
|
|||
|
Otherwise, the caller is printing a brief "summary" message. */
|
|||
|
|
|||
|
int
|
|||
|
print_symbol_loading_p (int from_tty, int exec, int full)
|
|||
|
{
|
|||
|
if (!from_tty && !info_verbose)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (exec)
|
|||
|
{
|
|||
|
/* We don't check FULL for executables, there are few such
|
|||
|
messages, therefore brief == full. */
|
|||
|
return print_symbol_loading != print_symbol_loading_off;
|
|||
|
}
|
|||
|
if (full)
|
|||
|
return print_symbol_loading == print_symbol_loading_full;
|
|||
|
return print_symbol_loading == print_symbol_loading_brief;
|
|||
|
}
|
|||
|
|
|||
|
/* True if we are reading a symbol table. */
|
|||
|
|
|||
|
int currently_reading_symtab = 0;
|
|||
|
|
|||
|
/* Increment currently_reading_symtab and return a cleanup that can be
|
|||
|
used to decrement it. */
|
|||
|
|
|||
|
scoped_restore_tmpl<int>
|
|||
|
increment_reading_symtab (void)
|
|||
|
{
|
|||
|
gdb_assert (currently_reading_symtab >= 0);
|
|||
|
return make_scoped_restore (¤tly_reading_symtab,
|
|||
|
currently_reading_symtab + 1);
|
|||
|
}
|
|||
|
|
|||
|
/* Remember the lowest-addressed loadable section we've seen.
|
|||
|
This function is called via bfd_map_over_sections.
|
|||
|
|
|||
|
In case of equal vmas, the section with the largest size becomes the
|
|||
|
lowest-addressed loadable section.
|
|||
|
|
|||
|
If the vmas and sizes are equal, the last section is considered the
|
|||
|
lowest-addressed loadable section. */
|
|||
|
|
|||
|
void
|
|||
|
find_lowest_section (bfd *abfd, asection *sect, void *obj)
|
|||
|
{
|
|||
|
asection **lowest = (asection **) obj;
|
|||
|
|
|||
|
if (0 == (bfd_section_flags (sect) & (SEC_ALLOC | SEC_LOAD)))
|
|||
|
return;
|
|||
|
if (!*lowest)
|
|||
|
*lowest = sect; /* First loadable section */
|
|||
|
else if (bfd_section_vma (*lowest) > bfd_section_vma (sect))
|
|||
|
*lowest = sect; /* A lower loadable section */
|
|||
|
else if (bfd_section_vma (*lowest) == bfd_section_vma (sect)
|
|||
|
&& (bfd_section_size (*lowest) <= bfd_section_size (sect)))
|
|||
|
*lowest = sect;
|
|||
|
}
|
|||
|
|
|||
|
/* Build (allocate and populate) a section_addr_info struct from
|
|||
|
an existing section table. */
|
|||
|
|
|||
|
section_addr_info
|
|||
|
build_section_addr_info_from_section_table (const struct target_section *start,
|
|||
|
const struct target_section *end)
|
|||
|
{
|
|||
|
const struct target_section *stp;
|
|||
|
|
|||
|
section_addr_info sap;
|
|||
|
|
|||
|
for (stp = start; stp != end; stp++)
|
|||
|
{
|
|||
|
struct bfd_section *asect = stp->the_bfd_section;
|
|||
|
bfd *abfd = asect->owner;
|
|||
|
|
|||
|
if (bfd_section_flags (asect) & (SEC_ALLOC | SEC_LOAD)
|
|||
|
&& sap.size () < end - start)
|
|||
|
sap.emplace_back (stp->addr,
|
|||
|
bfd_section_name (asect),
|
|||
|
gdb_bfd_section_index (abfd, asect));
|
|||
|
}
|
|||
|
|
|||
|
return sap;
|
|||
|
}
|
|||
|
|
|||
|
/* Create a section_addr_info from section offsets in ABFD. */
|
|||
|
|
|||
|
static section_addr_info
|
|||
|
build_section_addr_info_from_bfd (bfd *abfd)
|
|||
|
{
|
|||
|
struct bfd_section *sec;
|
|||
|
|
|||
|
section_addr_info sap;
|
|||
|
for (sec = abfd->sections; sec != NULL; sec = sec->next)
|
|||
|
if (bfd_section_flags (sec) & (SEC_ALLOC | SEC_LOAD))
|
|||
|
sap.emplace_back (bfd_section_vma (sec),
|
|||
|
bfd_section_name (sec),
|
|||
|
gdb_bfd_section_index (abfd, sec));
|
|||
|
|
|||
|
return sap;
|
|||
|
}
|
|||
|
|
|||
|
/* Create a section_addr_info from section offsets in OBJFILE. */
|
|||
|
|
|||
|
section_addr_info
|
|||
|
build_section_addr_info_from_objfile (const struct objfile *objfile)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
/* Before reread_symbols gets rewritten it is not safe to call:
|
|||
|
gdb_assert (objfile->num_sections == bfd_count_sections (objfile->obfd));
|
|||
|
*/
|
|||
|
section_addr_info sap = build_section_addr_info_from_bfd (objfile->obfd);
|
|||
|
for (i = 0; i < sap.size (); i++)
|
|||
|
{
|
|||
|
int sectindex = sap[i].sectindex;
|
|||
|
|
|||
|
sap[i].addr += objfile->section_offsets->offsets[sectindex];
|
|||
|
}
|
|||
|
return sap;
|
|||
|
}
|
|||
|
|
|||
|
/* Initialize OBJFILE's sect_index_* members. */
|
|||
|
|
|||
|
static void
|
|||
|
init_objfile_sect_indices (struct objfile *objfile)
|
|||
|
{
|
|||
|
asection *sect;
|
|||
|
int i;
|
|||
|
|
|||
|
sect = bfd_get_section_by_name (objfile->obfd, ".text");
|
|||
|
if (sect)
|
|||
|
objfile->sect_index_text = sect->index;
|
|||
|
|
|||
|
sect = bfd_get_section_by_name (objfile->obfd, ".data");
|
|||
|
if (sect)
|
|||
|
objfile->sect_index_data = sect->index;
|
|||
|
|
|||
|
sect = bfd_get_section_by_name (objfile->obfd, ".bss");
|
|||
|
if (sect)
|
|||
|
objfile->sect_index_bss = sect->index;
|
|||
|
|
|||
|
sect = bfd_get_section_by_name (objfile->obfd, ".rodata");
|
|||
|
if (sect)
|
|||
|
objfile->sect_index_rodata = sect->index;
|
|||
|
|
|||
|
/* This is where things get really weird... We MUST have valid
|
|||
|
indices for the various sect_index_* members or gdb will abort.
|
|||
|
So if for example, there is no ".text" section, we have to
|
|||
|
accomodate that. First, check for a file with the standard
|
|||
|
one or two segments. */
|
|||
|
|
|||
|
symfile_find_segment_sections (objfile);
|
|||
|
|
|||
|
/* Except when explicitly adding symbol files at some address,
|
|||
|
section_offsets contains nothing but zeros, so it doesn't matter
|
|||
|
which slot in section_offsets the individual sect_index_* members
|
|||
|
index into. So if they are all zero, it is safe to just point
|
|||
|
all the currently uninitialized indices to the first slot. But
|
|||
|
beware: if this is the main executable, it may be relocated
|
|||
|
later, e.g. by the remote qOffsets packet, and then this will
|
|||
|
be wrong! That's why we try segments first. */
|
|||
|
|
|||
|
for (i = 0; i < objfile->num_sections; i++)
|
|||
|
{
|
|||
|
if (ANOFFSET (objfile->section_offsets, i) != 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (i == objfile->num_sections)
|
|||
|
{
|
|||
|
if (objfile->sect_index_text == -1)
|
|||
|
objfile->sect_index_text = 0;
|
|||
|
if (objfile->sect_index_data == -1)
|
|||
|
objfile->sect_index_data = 0;
|
|||
|
if (objfile->sect_index_bss == -1)
|
|||
|
objfile->sect_index_bss = 0;
|
|||
|
if (objfile->sect_index_rodata == -1)
|
|||
|
objfile->sect_index_rodata = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* The arguments to place_section. */
|
|||
|
|
|||
|
struct place_section_arg
|
|||
|
{
|
|||
|
struct section_offsets *offsets;
|
|||
|
CORE_ADDR lowest;
|
|||
|
};
|
|||
|
|
|||
|
/* Find a unique offset to use for loadable section SECT if
|
|||
|
the user did not provide an offset. */
|
|||
|
|
|||
|
static void
|
|||
|
place_section (bfd *abfd, asection *sect, void *obj)
|
|||
|
{
|
|||
|
struct place_section_arg *arg = (struct place_section_arg *) obj;
|
|||
|
CORE_ADDR *offsets = arg->offsets->offsets, start_addr;
|
|||
|
int done;
|
|||
|
ULONGEST align = ((ULONGEST) 1) << bfd_section_alignment (sect);
|
|||
|
|
|||
|
/* We are only interested in allocated sections. */
|
|||
|
if ((bfd_section_flags (sect) & SEC_ALLOC) == 0)
|
|||
|
return;
|
|||
|
|
|||
|
/* If the user specified an offset, honor it. */
|
|||
|
if (offsets[gdb_bfd_section_index (abfd, sect)] != 0)
|
|||
|
return;
|
|||
|
|
|||
|
/* Otherwise, let's try to find a place for the section. */
|
|||
|
start_addr = (arg->lowest + align - 1) & -align;
|
|||
|
|
|||
|
do {
|
|||
|
asection *cur_sec;
|
|||
|
|
|||
|
done = 1;
|
|||
|
|
|||
|
for (cur_sec = abfd->sections; cur_sec != NULL; cur_sec = cur_sec->next)
|
|||
|
{
|
|||
|
int indx = cur_sec->index;
|
|||
|
|
|||
|
/* We don't need to compare against ourself. */
|
|||
|
if (cur_sec == sect)
|
|||
|
continue;
|
|||
|
|
|||
|
/* We can only conflict with allocated sections. */
|
|||
|
if ((bfd_section_flags (cur_sec) & SEC_ALLOC) == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
/* If the section offset is 0, either the section has not been placed
|
|||
|
yet, or it was the lowest section placed (in which case LOWEST
|
|||
|
will be past its end). */
|
|||
|
if (offsets[indx] == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
/* If this section would overlap us, then we must move up. */
|
|||
|
if (start_addr + bfd_section_size (sect) > offsets[indx]
|
|||
|
&& start_addr < offsets[indx] + bfd_section_size (cur_sec))
|
|||
|
{
|
|||
|
start_addr = offsets[indx] + bfd_section_size (cur_sec);
|
|||
|
start_addr = (start_addr + align - 1) & -align;
|
|||
|
done = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* Otherwise, we appear to be OK. So far. */
|
|||
|
}
|
|||
|
}
|
|||
|
while (!done);
|
|||
|
|
|||
|
offsets[gdb_bfd_section_index (abfd, sect)] = start_addr;
|
|||
|
arg->lowest = start_addr + bfd_section_size (sect);
|
|||
|
}
|
|||
|
|
|||
|
/* Store section_addr_info as prepared (made relative and with SECTINDEX
|
|||
|
filled-in) by addr_info_make_relative into SECTION_OFFSETS of NUM_SECTIONS
|
|||
|
entries. */
|
|||
|
|
|||
|
void
|
|||
|
relative_addr_info_to_section_offsets (struct section_offsets *section_offsets,
|
|||
|
int num_sections,
|
|||
|
const section_addr_info &addrs)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
memset (section_offsets, 0, SIZEOF_N_SECTION_OFFSETS (num_sections));
|
|||
|
|
|||
|
/* Now calculate offsets for section that were specified by the caller. */
|
|||
|
for (i = 0; i < addrs.size (); i++)
|
|||
|
{
|
|||
|
const struct other_sections *osp;
|
|||
|
|
|||
|
osp = &addrs[i];
|
|||
|
if (osp->sectindex == -1)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Record all sections in offsets. */
|
|||
|
/* The section_offsets in the objfile are here filled in using
|
|||
|
the BFD index. */
|
|||
|
section_offsets->offsets[osp->sectindex] = osp->addr;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Transform section name S for a name comparison. prelink can split section
|
|||
|
`.bss' into two sections `.dynbss' and `.bss' (in this order). Similarly
|
|||
|
prelink can split `.sbss' into `.sdynbss' and `.sbss'. Use virtual address
|
|||
|
of the new `.dynbss' (`.sdynbss') section as the adjacent new `.bss'
|
|||
|
(`.sbss') section has invalid (increased) virtual address. */
|
|||
|
|
|||
|
static const char *
|
|||
|
addr_section_name (const char *s)
|
|||
|
{
|
|||
|
if (strcmp (s, ".dynbss") == 0)
|
|||
|
return ".bss";
|
|||
|
if (strcmp (s, ".sdynbss") == 0)
|
|||
|
return ".sbss";
|
|||
|
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
/* std::sort comparator for addrs_section_sort. Sort entries in
|
|||
|
ascending order by their (name, sectindex) pair. sectindex makes
|
|||
|
the sort by name stable. */
|
|||
|
|
|||
|
static bool
|
|||
|
addrs_section_compar (const struct other_sections *a,
|
|||
|
const struct other_sections *b)
|
|||
|
{
|
|||
|
int retval;
|
|||
|
|
|||
|
retval = strcmp (addr_section_name (a->name.c_str ()),
|
|||
|
addr_section_name (b->name.c_str ()));
|
|||
|
if (retval != 0)
|
|||
|
return retval < 0;
|
|||
|
|
|||
|
return a->sectindex < b->sectindex;
|
|||
|
}
|
|||
|
|
|||
|
/* Provide sorted array of pointers to sections of ADDRS. */
|
|||
|
|
|||
|
static std::vector<const struct other_sections *>
|
|||
|
addrs_section_sort (const section_addr_info &addrs)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
std::vector<const struct other_sections *> array (addrs.size ());
|
|||
|
for (i = 0; i < addrs.size (); i++)
|
|||
|
array[i] = &addrs[i];
|
|||
|
|
|||
|
std::sort (array.begin (), array.end (), addrs_section_compar);
|
|||
|
|
|||
|
return array;
|
|||
|
}
|
|||
|
|
|||
|
/* Relativize absolute addresses in ADDRS into offsets based on ABFD. Fill-in
|
|||
|
also SECTINDEXes specific to ABFD there. This function can be used to
|
|||
|
rebase ADDRS to start referencing different BFD than before. */
|
|||
|
|
|||
|
void
|
|||
|
addr_info_make_relative (section_addr_info *addrs, bfd *abfd)
|
|||
|
{
|
|||
|
asection *lower_sect;
|
|||
|
CORE_ADDR lower_offset;
|
|||
|
int i;
|
|||
|
|
|||
|
/* Find lowest loadable section to be used as starting point for
|
|||
|
contiguous sections. */
|
|||
|
lower_sect = NULL;
|
|||
|
bfd_map_over_sections (abfd, find_lowest_section, &lower_sect);
|
|||
|
if (lower_sect == NULL)
|
|||
|
{
|
|||
|
warning (_("no loadable sections found in added symbol-file %s"),
|
|||
|
bfd_get_filename (abfd));
|
|||
|
lower_offset = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
lower_offset = bfd_section_vma (lower_sect);
|
|||
|
|
|||
|
/* Create ADDRS_TO_ABFD_ADDRS array to map the sections in ADDRS to sections
|
|||
|
in ABFD. Section names are not unique - there can be multiple sections of
|
|||
|
the same name. Also the sections of the same name do not have to be
|
|||
|
adjacent to each other. Some sections may be present only in one of the
|
|||
|
files. Even sections present in both files do not have to be in the same
|
|||
|
order.
|
|||
|
|
|||
|
Use stable sort by name for the sections in both files. Then linearly
|
|||
|
scan both lists matching as most of the entries as possible. */
|
|||
|
|
|||
|
std::vector<const struct other_sections *> addrs_sorted
|
|||
|
= addrs_section_sort (*addrs);
|
|||
|
|
|||
|
section_addr_info abfd_addrs = build_section_addr_info_from_bfd (abfd);
|
|||
|
std::vector<const struct other_sections *> abfd_addrs_sorted
|
|||
|
= addrs_section_sort (abfd_addrs);
|
|||
|
|
|||
|
/* Now create ADDRS_TO_ABFD_ADDRS from ADDRS_SORTED and
|
|||
|
ABFD_ADDRS_SORTED. */
|
|||
|
|
|||
|
std::vector<const struct other_sections *>
|
|||
|
addrs_to_abfd_addrs (addrs->size (), nullptr);
|
|||
|
|
|||
|
std::vector<const struct other_sections *>::iterator abfd_sorted_iter
|
|||
|
= abfd_addrs_sorted.begin ();
|
|||
|
for (const other_sections *sect : addrs_sorted)
|
|||
|
{
|
|||
|
const char *sect_name = addr_section_name (sect->name.c_str ());
|
|||
|
|
|||
|
while (abfd_sorted_iter != abfd_addrs_sorted.end ()
|
|||
|
&& strcmp (addr_section_name ((*abfd_sorted_iter)->name.c_str ()),
|
|||
|
sect_name) < 0)
|
|||
|
abfd_sorted_iter++;
|
|||
|
|
|||
|
if (abfd_sorted_iter != abfd_addrs_sorted.end ()
|
|||
|
&& strcmp (addr_section_name ((*abfd_sorted_iter)->name.c_str ()),
|
|||
|
sect_name) == 0)
|
|||
|
{
|
|||
|
int index_in_addrs;
|
|||
|
|
|||
|
/* Make the found item directly addressable from ADDRS. */
|
|||
|
index_in_addrs = sect - addrs->data ();
|
|||
|
gdb_assert (addrs_to_abfd_addrs[index_in_addrs] == NULL);
|
|||
|
addrs_to_abfd_addrs[index_in_addrs] = *abfd_sorted_iter;
|
|||
|
|
|||
|
/* Never use the same ABFD entry twice. */
|
|||
|
abfd_sorted_iter++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Calculate offsets for the loadable sections.
|
|||
|
FIXME! Sections must be in order of increasing loadable section
|
|||
|
so that contiguous sections can use the lower-offset!!!
|
|||
|
|
|||
|
Adjust offsets if the segments are not contiguous.
|
|||
|
If the section is contiguous, its offset should be set to
|
|||
|
the offset of the highest loadable section lower than it
|
|||
|
(the loadable section directly below it in memory).
|
|||
|
this_offset = lower_offset = lower_addr - lower_orig_addr */
|
|||
|
|
|||
|
for (i = 0; i < addrs->size (); i++)
|
|||
|
{
|
|||
|
const struct other_sections *sect = addrs_to_abfd_addrs[i];
|
|||
|
|
|||
|
if (sect)
|
|||
|
{
|
|||
|
/* This is the index used by BFD. */
|
|||
|
(*addrs)[i].sectindex = sect->sectindex;
|
|||
|
|
|||
|
if ((*addrs)[i].addr != 0)
|
|||
|
{
|
|||
|
(*addrs)[i].addr -= sect->addr;
|
|||
|
lower_offset = (*addrs)[i].addr;
|
|||
|
}
|
|||
|
else
|
|||
|
(*addrs)[i].addr = lower_offset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* addr_section_name transformation is not used for SECT_NAME. */
|
|||
|
const std::string §_name = (*addrs)[i].name;
|
|||
|
|
|||
|
/* This section does not exist in ABFD, which is normally
|
|||
|
unexpected and we want to issue a warning.
|
|||
|
|
|||
|
However, the ELF prelinker does create a few sections which are
|
|||
|
marked in the main executable as loadable (they are loaded in
|
|||
|
memory from the DYNAMIC segment) and yet are not present in
|
|||
|
separate debug info files. This is fine, and should not cause
|
|||
|
a warning. Shared libraries contain just the section
|
|||
|
".gnu.liblist" but it is not marked as loadable there. There is
|
|||
|
no other way to identify them than by their name as the sections
|
|||
|
created by prelink have no special flags.
|
|||
|
|
|||
|
For the sections `.bss' and `.sbss' see addr_section_name. */
|
|||
|
|
|||
|
if (!(sect_name == ".gnu.liblist"
|
|||
|
|| sect_name == ".gnu.conflict"
|
|||
|
|| (sect_name == ".bss"
|
|||
|
&& i > 0
|
|||
|
&& (*addrs)[i - 1].name == ".dynbss"
|
|||
|
&& addrs_to_abfd_addrs[i - 1] != NULL)
|
|||
|
|| (sect_name == ".sbss"
|
|||
|
&& i > 0
|
|||
|
&& (*addrs)[i - 1].name == ".sdynbss"
|
|||
|
&& addrs_to_abfd_addrs[i - 1] != NULL)))
|
|||
|
warning (_("section %s not found in %s"), sect_name.c_str (),
|
|||
|
bfd_get_filename (abfd));
|
|||
|
|
|||
|
(*addrs)[i].addr = 0;
|
|||
|
(*addrs)[i].sectindex = -1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Parse the user's idea of an offset for dynamic linking, into our idea
|
|||
|
of how to represent it for fast symbol reading. This is the default
|
|||
|
version of the sym_fns.sym_offsets function for symbol readers that
|
|||
|
don't need to do anything special. It allocates a section_offsets table
|
|||
|
for the objectfile OBJFILE and stuffs ADDR into all of the offsets. */
|
|||
|
|
|||
|
void
|
|||
|
default_symfile_offsets (struct objfile *objfile,
|
|||
|
const section_addr_info &addrs)
|
|||
|
{
|
|||
|
objfile->num_sections = gdb_bfd_count_sections (objfile->obfd);
|
|||
|
objfile->section_offsets = (struct section_offsets *)
|
|||
|
obstack_alloc (&objfile->objfile_obstack,
|
|||
|
SIZEOF_N_SECTION_OFFSETS (objfile->num_sections));
|
|||
|
relative_addr_info_to_section_offsets (objfile->section_offsets,
|
|||
|
objfile->num_sections, addrs);
|
|||
|
|
|||
|
/* For relocatable files, all loadable sections will start at zero.
|
|||
|
The zero is meaningless, so try to pick arbitrary addresses such
|
|||
|
that no loadable sections overlap. This algorithm is quadratic,
|
|||
|
but the number of sections in a single object file is generally
|
|||
|
small. */
|
|||
|
if ((bfd_get_file_flags (objfile->obfd) & (EXEC_P | DYNAMIC)) == 0)
|
|||
|
{
|
|||
|
struct place_section_arg arg;
|
|||
|
bfd *abfd = objfile->obfd;
|
|||
|
asection *cur_sec;
|
|||
|
|
|||
|
for (cur_sec = abfd->sections; cur_sec != NULL; cur_sec = cur_sec->next)
|
|||
|
/* We do not expect this to happen; just skip this step if the
|
|||
|
relocatable file has a section with an assigned VMA. */
|
|||
|
if (bfd_section_vma (cur_sec) != 0)
|
|||
|
break;
|
|||
|
|
|||
|
if (cur_sec == NULL)
|
|||
|
{
|
|||
|
CORE_ADDR *offsets = objfile->section_offsets->offsets;
|
|||
|
|
|||
|
/* Pick non-overlapping offsets for sections the user did not
|
|||
|
place explicitly. */
|
|||
|
arg.offsets = objfile->section_offsets;
|
|||
|
arg.lowest = 0;
|
|||
|
bfd_map_over_sections (objfile->obfd, place_section, &arg);
|
|||
|
|
|||
|
/* Correctly filling in the section offsets is not quite
|
|||
|
enough. Relocatable files have two properties that
|
|||
|
(most) shared objects do not:
|
|||
|
|
|||
|
- Their debug information will contain relocations. Some
|
|||
|
shared libraries do also, but many do not, so this can not
|
|||
|
be assumed.
|
|||
|
|
|||
|
- If there are multiple code sections they will be loaded
|
|||
|
at different relative addresses in memory than they are
|
|||
|
in the objfile, since all sections in the file will start
|
|||
|
at address zero.
|
|||
|
|
|||
|
Because GDB has very limited ability to map from an
|
|||
|
address in debug info to the correct code section,
|
|||
|
it relies on adding SECT_OFF_TEXT to things which might be
|
|||
|
code. If we clear all the section offsets, and set the
|
|||
|
section VMAs instead, then symfile_relocate_debug_section
|
|||
|
will return meaningful debug information pointing at the
|
|||
|
correct sections.
|
|||
|
|
|||
|
GDB has too many different data structures for section
|
|||
|
addresses - a bfd, objfile, and so_list all have section
|
|||
|
tables, as does exec_ops. Some of these could probably
|
|||
|
be eliminated. */
|
|||
|
|
|||
|
for (cur_sec = abfd->sections; cur_sec != NULL;
|
|||
|
cur_sec = cur_sec->next)
|
|||
|
{
|
|||
|
if ((bfd_section_flags (cur_sec) & SEC_ALLOC) == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
bfd_set_section_vma (cur_sec, offsets[cur_sec->index]);
|
|||
|
exec_set_section_address (bfd_get_filename (abfd),
|
|||
|
cur_sec->index,
|
|||
|
offsets[cur_sec->index]);
|
|||
|
offsets[cur_sec->index] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Remember the bfd indexes for the .text, .data, .bss and
|
|||
|
.rodata sections. */
|
|||
|
init_objfile_sect_indices (objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Divide the file into segments, which are individual relocatable units.
|
|||
|
This is the default version of the sym_fns.sym_segments function for
|
|||
|
symbol readers that do not have an explicit representation of segments.
|
|||
|
It assumes that object files do not have segments, and fully linked
|
|||
|
files have a single segment. */
|
|||
|
|
|||
|
struct symfile_segment_data *
|
|||
|
default_symfile_segments (bfd *abfd)
|
|||
|
{
|
|||
|
int num_sections, i;
|
|||
|
asection *sect;
|
|||
|
struct symfile_segment_data *data;
|
|||
|
CORE_ADDR low, high;
|
|||
|
|
|||
|
/* Relocatable files contain enough information to position each
|
|||
|
loadable section independently; they should not be relocated
|
|||
|
in segments. */
|
|||
|
if ((bfd_get_file_flags (abfd) & (EXEC_P | DYNAMIC)) == 0)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* Make sure there is at least one loadable section in the file. */
|
|||
|
for (sect = abfd->sections; sect != NULL; sect = sect->next)
|
|||
|
{
|
|||
|
if ((bfd_section_flags (sect) & SEC_ALLOC) == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
if (sect == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
low = bfd_section_vma (sect);
|
|||
|
high = low + bfd_section_size (sect);
|
|||
|
|
|||
|
data = XCNEW (struct symfile_segment_data);
|
|||
|
data->num_segments = 1;
|
|||
|
data->segment_bases = XCNEW (CORE_ADDR);
|
|||
|
data->segment_sizes = XCNEW (CORE_ADDR);
|
|||
|
|
|||
|
num_sections = bfd_count_sections (abfd);
|
|||
|
data->segment_info = XCNEWVEC (int, num_sections);
|
|||
|
|
|||
|
for (i = 0, sect = abfd->sections; sect != NULL; i++, sect = sect->next)
|
|||
|
{
|
|||
|
CORE_ADDR vma;
|
|||
|
|
|||
|
if ((bfd_section_flags (sect) & SEC_ALLOC) == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
vma = bfd_section_vma (sect);
|
|||
|
if (vma < low)
|
|||
|
low = vma;
|
|||
|
if (vma + bfd_section_size (sect) > high)
|
|||
|
high = vma + bfd_section_size (sect);
|
|||
|
|
|||
|
data->segment_info[i] = 1;
|
|||
|
}
|
|||
|
|
|||
|
data->segment_bases[0] = low;
|
|||
|
data->segment_sizes[0] = high - low;
|
|||
|
|
|||
|
return data;
|
|||
|
}
|
|||
|
|
|||
|
/* This is a convenience function to call sym_read for OBJFILE and
|
|||
|
possibly force the partial symbols to be read. */
|
|||
|
|
|||
|
static void
|
|||
|
read_symbols (struct objfile *objfile, symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
(*objfile->sf->sym_read) (objfile, add_flags);
|
|||
|
objfile->per_bfd->minsyms_read = true;
|
|||
|
|
|||
|
/* find_separate_debug_file_in_section should be called only if there is
|
|||
|
single binary with no existing separate debug info file. */
|
|||
|
if (!objfile_has_partial_symbols (objfile)
|
|||
|
&& objfile->separate_debug_objfile == NULL
|
|||
|
&& objfile->separate_debug_objfile_backlink == NULL)
|
|||
|
{
|
|||
|
gdb_bfd_ref_ptr abfd (find_separate_debug_file_in_section (objfile));
|
|||
|
|
|||
|
if (abfd != NULL)
|
|||
|
{
|
|||
|
/* find_separate_debug_file_in_section uses the same filename for the
|
|||
|
virtual section-as-bfd like the bfd filename containing the
|
|||
|
section. Therefore use also non-canonical name form for the same
|
|||
|
file containing the section. */
|
|||
|
symbol_file_add_separate (abfd.get (),
|
|||
|
bfd_get_filename (abfd.get ()),
|
|||
|
add_flags | SYMFILE_NOT_FILENAME, objfile);
|
|||
|
}
|
|||
|
}
|
|||
|
if ((add_flags & SYMFILE_NO_READ) == 0)
|
|||
|
require_partial_symbols (objfile, false);
|
|||
|
}
|
|||
|
|
|||
|
/* Initialize entry point information for this objfile. */
|
|||
|
|
|||
|
static void
|
|||
|
init_entry_point_info (struct objfile *objfile)
|
|||
|
{
|
|||
|
struct entry_info *ei = &objfile->per_bfd->ei;
|
|||
|
|
|||
|
if (ei->initialized)
|
|||
|
return;
|
|||
|
ei->initialized = 1;
|
|||
|
|
|||
|
/* Save startup file's range of PC addresses to help blockframe.c
|
|||
|
decide where the bottom of the stack is. */
|
|||
|
|
|||
|
if (bfd_get_file_flags (objfile->obfd) & EXEC_P)
|
|||
|
{
|
|||
|
/* Executable file -- record its entry point so we'll recognize
|
|||
|
the startup file because it contains the entry point. */
|
|||
|
ei->entry_point = bfd_get_start_address (objfile->obfd);
|
|||
|
ei->entry_point_p = 1;
|
|||
|
}
|
|||
|
else if (bfd_get_file_flags (objfile->obfd) & DYNAMIC
|
|||
|
&& bfd_get_start_address (objfile->obfd) != 0)
|
|||
|
{
|
|||
|
/* Some shared libraries may have entry points set and be
|
|||
|
runnable. There's no clear way to indicate this, so just check
|
|||
|
for values other than zero. */
|
|||
|
ei->entry_point = bfd_get_start_address (objfile->obfd);
|
|||
|
ei->entry_point_p = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Examination of non-executable.o files. Short-circuit this stuff. */
|
|||
|
ei->entry_point_p = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (ei->entry_point_p)
|
|||
|
{
|
|||
|
struct obj_section *osect;
|
|||
|
CORE_ADDR entry_point = ei->entry_point;
|
|||
|
int found;
|
|||
|
|
|||
|
/* Make certain that the address points at real code, and not a
|
|||
|
function descriptor. */
|
|||
|
entry_point
|
|||
|
= gdbarch_convert_from_func_ptr_addr (get_objfile_arch (objfile),
|
|||
|
entry_point,
|
|||
|
current_top_target ());
|
|||
|
|
|||
|
/* Remove any ISA markers, so that this matches entries in the
|
|||
|
symbol table. */
|
|||
|
ei->entry_point
|
|||
|
= gdbarch_addr_bits_remove (get_objfile_arch (objfile), entry_point);
|
|||
|
|
|||
|
found = 0;
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, osect)
|
|||
|
{
|
|||
|
struct bfd_section *sect = osect->the_bfd_section;
|
|||
|
|
|||
|
if (entry_point >= bfd_section_vma (sect)
|
|||
|
&& entry_point < (bfd_section_vma (sect)
|
|||
|
+ bfd_section_size (sect)))
|
|||
|
{
|
|||
|
ei->the_bfd_section_index
|
|||
|
= gdb_bfd_section_index (objfile->obfd, sect);
|
|||
|
found = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!found)
|
|||
|
ei->the_bfd_section_index = SECT_OFF_TEXT (objfile);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Process a symbol file, as either the main file or as a dynamically
|
|||
|
loaded file.
|
|||
|
|
|||
|
This function does not set the OBJFILE's entry-point info.
|
|||
|
|
|||
|
OBJFILE is where the symbols are to be read from.
|
|||
|
|
|||
|
ADDRS is the list of section load addresses. If the user has given
|
|||
|
an 'add-symbol-file' command, then this is the list of offsets and
|
|||
|
addresses he or she provided as arguments to the command; or, if
|
|||
|
we're handling a shared library, these are the actual addresses the
|
|||
|
sections are loaded at, according to the inferior's dynamic linker
|
|||
|
(as gleaned by GDB's shared library code). We convert each address
|
|||
|
into an offset from the section VMA's as it appears in the object
|
|||
|
file, and then call the file's sym_offsets function to convert this
|
|||
|
into a format-specific offset table --- a `struct section_offsets'.
|
|||
|
The sectindex field is used to control the ordering of sections
|
|||
|
with the same name. Upon return, it is updated to contain the
|
|||
|
corresponding BFD section index, or -1 if the section was not found.
|
|||
|
|
|||
|
ADD_FLAGS encodes verbosity level, whether this is main symbol or
|
|||
|
an extra symbol file such as dynamically loaded code, and whether
|
|||
|
breakpoint reset should be deferred. */
|
|||
|
|
|||
|
static void
|
|||
|
syms_from_objfile_1 (struct objfile *objfile,
|
|||
|
section_addr_info *addrs,
|
|||
|
symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
section_addr_info local_addr;
|
|||
|
const int mainline = add_flags & SYMFILE_MAINLINE;
|
|||
|
|
|||
|
objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd));
|
|||
|
|
|||
|
if (objfile->sf == NULL)
|
|||
|
{
|
|||
|
/* No symbols to load, but we still need to make sure
|
|||
|
that the section_offsets table is allocated. */
|
|||
|
int num_sections = gdb_bfd_count_sections (objfile->obfd);
|
|||
|
size_t size = SIZEOF_N_SECTION_OFFSETS (num_sections);
|
|||
|
|
|||
|
objfile->num_sections = num_sections;
|
|||
|
objfile->section_offsets
|
|||
|
= (struct section_offsets *) obstack_alloc (&objfile->objfile_obstack,
|
|||
|
size);
|
|||
|
memset (objfile->section_offsets, 0, size);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Make sure that partially constructed symbol tables will be cleaned up
|
|||
|
if an error occurs during symbol reading. */
|
|||
|
gdb::optional<clear_symtab_users_cleanup> defer_clear_users;
|
|||
|
|
|||
|
std::unique_ptr<struct objfile> objfile_holder (objfile);
|
|||
|
|
|||
|
/* If ADDRS is NULL, put together a dummy address list.
|
|||
|
We now establish the convention that an addr of zero means
|
|||
|
no load address was specified. */
|
|||
|
if (! addrs)
|
|||
|
addrs = &local_addr;
|
|||
|
|
|||
|
if (mainline)
|
|||
|
{
|
|||
|
/* We will modify the main symbol table, make sure that all its users
|
|||
|
will be cleaned up if an error occurs during symbol reading. */
|
|||
|
defer_clear_users.emplace ((symfile_add_flag) 0);
|
|||
|
|
|||
|
/* Since no error yet, throw away the old symbol table. */
|
|||
|
|
|||
|
if (symfile_objfile != NULL)
|
|||
|
{
|
|||
|
delete symfile_objfile;
|
|||
|
gdb_assert (symfile_objfile == NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* Currently we keep symbols from the add-symbol-file command.
|
|||
|
If the user wants to get rid of them, they should do "symbol-file"
|
|||
|
without arguments first. Not sure this is the best behavior
|
|||
|
(PR 2207). */
|
|||
|
|
|||
|
(*objfile->sf->sym_new_init) (objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Convert addr into an offset rather than an absolute address.
|
|||
|
We find the lowest address of a loaded segment in the objfile,
|
|||
|
and assume that <addr> is where that got loaded.
|
|||
|
|
|||
|
We no longer warn if the lowest section is not a text segment (as
|
|||
|
happens for the PA64 port. */
|
|||
|
if (addrs->size () > 0)
|
|||
|
addr_info_make_relative (addrs, objfile->obfd);
|
|||
|
|
|||
|
/* Initialize symbol reading routines for this objfile, allow complaints to
|
|||
|
appear for this new file, and record how verbose to be, then do the
|
|||
|
initial symbol reading for this file. */
|
|||
|
|
|||
|
(*objfile->sf->sym_init) (objfile);
|
|||
|
clear_complaints ();
|
|||
|
|
|||
|
(*objfile->sf->sym_offsets) (objfile, *addrs);
|
|||
|
|
|||
|
read_symbols (objfile, add_flags);
|
|||
|
|
|||
|
/* Discard cleanups as symbol reading was successful. */
|
|||
|
|
|||
|
objfile_holder.release ();
|
|||
|
if (defer_clear_users)
|
|||
|
defer_clear_users->release ();
|
|||
|
}
|
|||
|
|
|||
|
/* Same as syms_from_objfile_1, but also initializes the objfile
|
|||
|
entry-point info. */
|
|||
|
|
|||
|
static void
|
|||
|
syms_from_objfile (struct objfile *objfile,
|
|||
|
section_addr_info *addrs,
|
|||
|
symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
syms_from_objfile_1 (objfile, addrs, add_flags);
|
|||
|
init_entry_point_info (objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Perform required actions after either reading in the initial
|
|||
|
symbols for a new objfile, or mapping in the symbols from a reusable
|
|||
|
objfile. ADD_FLAGS is a bitmask of enum symfile_add_flags. */
|
|||
|
|
|||
|
static void
|
|||
|
finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
/* If this is the main symbol file we have to clean up all users of the
|
|||
|
old main symbol file. Otherwise it is sufficient to fixup all the
|
|||
|
breakpoints that may have been redefined by this symbol file. */
|
|||
|
if (add_flags & SYMFILE_MAINLINE)
|
|||
|
{
|
|||
|
/* OK, make it the "real" symbol file. */
|
|||
|
symfile_objfile = objfile;
|
|||
|
|
|||
|
clear_symtab_users (add_flags);
|
|||
|
}
|
|||
|
else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
|
|||
|
{
|
|||
|
breakpoint_re_set ();
|
|||
|
}
|
|||
|
|
|||
|
/* We're done reading the symbol file; finish off complaints. */
|
|||
|
clear_complaints ();
|
|||
|
}
|
|||
|
|
|||
|
/* Process a symbol file, as either the main file or as a dynamically
|
|||
|
loaded file.
|
|||
|
|
|||
|
ABFD is a BFD already open on the file, as from symfile_bfd_open.
|
|||
|
A new reference is acquired by this function.
|
|||
|
|
|||
|
For NAME description see the objfile constructor.
|
|||
|
|
|||
|
ADD_FLAGS encodes verbosity, whether this is main symbol file or
|
|||
|
extra, such as dynamically loaded code, and what to do with breakpoints.
|
|||
|
|
|||
|
ADDRS is as described for syms_from_objfile_1, above.
|
|||
|
ADDRS is ignored when SYMFILE_MAINLINE bit is set in ADD_FLAGS.
|
|||
|
|
|||
|
PARENT is the original objfile if ABFD is a separate debug info file.
|
|||
|
Otherwise PARENT is NULL.
|
|||
|
|
|||
|
Upon success, returns a pointer to the objfile that was added.
|
|||
|
Upon failure, jumps back to command level (never returns). */
|
|||
|
|
|||
|
static struct objfile *
|
|||
|
symbol_file_add_with_addrs (bfd *abfd, const char *name,
|
|||
|
symfile_add_flags add_flags,
|
|||
|
section_addr_info *addrs,
|
|||
|
objfile_flags flags, struct objfile *parent)
|
|||
|
{
|
|||
|
struct objfile *objfile;
|
|||
|
const int from_tty = add_flags & SYMFILE_VERBOSE;
|
|||
|
const int mainline = add_flags & SYMFILE_MAINLINE;
|
|||
|
const int should_print = (print_symbol_loading_p (from_tty, mainline, 1)
|
|||
|
&& (readnow_symbol_files
|
|||
|
|| (add_flags & SYMFILE_NO_READ) == 0));
|
|||
|
|
|||
|
if (readnow_symbol_files)
|
|||
|
{
|
|||
|
flags |= OBJF_READNOW;
|
|||
|
add_flags &= ~SYMFILE_NO_READ;
|
|||
|
}
|
|||
|
else if (readnever_symbol_files
|
|||
|
|| (parent != NULL && (parent->flags & OBJF_READNEVER)))
|
|||
|
{
|
|||
|
flags |= OBJF_READNEVER;
|
|||
|
add_flags |= SYMFILE_NO_READ;
|
|||
|
}
|
|||
|
if ((add_flags & SYMFILE_NOT_FILENAME) != 0)
|
|||
|
flags |= OBJF_NOT_FILENAME;
|
|||
|
|
|||
|
/* Give user a chance to burp if we'd be
|
|||
|
interactively wiping out any existing symbols. */
|
|||
|
|
|||
|
if ((have_full_symbols () || have_partial_symbols ())
|
|||
|
&& mainline
|
|||
|
&& from_tty
|
|||
|
&& !query (_("Load new symbol table from \"%s\"? "), name))
|
|||
|
error (_("Not confirmed."));
|
|||
|
|
|||
|
if (mainline)
|
|||
|
flags |= OBJF_MAINLINE;
|
|||
|
objfile = new struct objfile (abfd, name, flags);
|
|||
|
|
|||
|
if (parent)
|
|||
|
add_separate_debug_objfile (objfile, parent);
|
|||
|
|
|||
|
/* We either created a new mapped symbol table, mapped an existing
|
|||
|
symbol table file which has not had initial symbol reading
|
|||
|
performed, or need to read an unmapped symbol table. */
|
|||
|
if (should_print)
|
|||
|
{
|
|||
|
if (deprecated_pre_add_symbol_hook)
|
|||
|
deprecated_pre_add_symbol_hook (name);
|
|||
|
else
|
|||
|
printf_filtered (_("Reading symbols from %ps...\n"),
|
|||
|
styled_string (file_name_style.style (), name));
|
|||
|
}
|
|||
|
syms_from_objfile (objfile, addrs, add_flags);
|
|||
|
|
|||
|
/* We now have at least a partial symbol table. Check to see if the
|
|||
|
user requested that all symbols be read on initial access via either
|
|||
|
the gdb startup command line or on a per symbol file basis. Expand
|
|||
|
all partial symbol tables for this objfile if so. */
|
|||
|
|
|||
|
if ((flags & OBJF_READNOW))
|
|||
|
{
|
|||
|
if (should_print)
|
|||
|
printf_filtered (_("Expanding full symbols from %ps...\n"),
|
|||
|
styled_string (file_name_style.style (), name));
|
|||
|
|
|||
|
if (objfile->sf)
|
|||
|
objfile->sf->qf->expand_all_symtabs (objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Note that we only print a message if we have no symbols and have
|
|||
|
no separate debug file. If there is a separate debug file which
|
|||
|
does not have symbols, we'll have emitted this message for that
|
|||
|
file, and so printing it twice is just redundant. */
|
|||
|
if (should_print && !objfile_has_symbols (objfile)
|
|||
|
&& objfile->separate_debug_objfile == nullptr)
|
|||
|
printf_filtered (_("(No debugging symbols found in %ps)\n"),
|
|||
|
styled_string (file_name_style.style (), name));
|
|||
|
|
|||
|
if (should_print)
|
|||
|
{
|
|||
|
if (deprecated_post_add_symbol_hook)
|
|||
|
deprecated_post_add_symbol_hook ();
|
|||
|
}
|
|||
|
|
|||
|
/* We print some messages regardless of whether 'from_tty ||
|
|||
|
info_verbose' is true, so make sure they go out at the right
|
|||
|
time. */
|
|||
|
gdb_flush (gdb_stdout);
|
|||
|
|
|||
|
if (objfile->sf == NULL)
|
|||
|
{
|
|||
|
gdb::observers::new_objfile.notify (objfile);
|
|||
|
return objfile; /* No symbols. */
|
|||
|
}
|
|||
|
|
|||
|
finish_new_objfile (objfile, add_flags);
|
|||
|
|
|||
|
gdb::observers::new_objfile.notify (objfile);
|
|||
|
|
|||
|
bfd_cache_close_all ();
|
|||
|
return (objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Add BFD as a separate debug file for OBJFILE. For NAME description
|
|||
|
see the objfile constructor. */
|
|||
|
|
|||
|
void
|
|||
|
symbol_file_add_separate (bfd *bfd, const char *name,
|
|||
|
symfile_add_flags symfile_flags,
|
|||
|
struct objfile *objfile)
|
|||
|
{
|
|||
|
/* Create section_addr_info. We can't directly use offsets from OBJFILE
|
|||
|
because sections of BFD may not match sections of OBJFILE and because
|
|||
|
vma may have been modified by tools such as prelink. */
|
|||
|
section_addr_info sap = build_section_addr_info_from_objfile (objfile);
|
|||
|
|
|||
|
symbol_file_add_with_addrs
|
|||
|
(bfd, name, symfile_flags, &sap,
|
|||
|
objfile->flags & (OBJF_REORDERED | OBJF_SHARED | OBJF_READNOW
|
|||
|
| OBJF_USERLOADED | OBJF_MAINLINE),
|
|||
|
objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Process the symbol file ABFD, as either the main file or as a
|
|||
|
dynamically loaded file.
|
|||
|
See symbol_file_add_with_addrs's comments for details. */
|
|||
|
|
|||
|
struct objfile *
|
|||
|
symbol_file_add_from_bfd (bfd *abfd, const char *name,
|
|||
|
symfile_add_flags add_flags,
|
|||
|
section_addr_info *addrs,
|
|||
|
objfile_flags flags, struct objfile *parent)
|
|||
|
{
|
|||
|
return symbol_file_add_with_addrs (abfd, name, add_flags, addrs, flags,
|
|||
|
parent);
|
|||
|
}
|
|||
|
|
|||
|
/* Process a symbol file, as either the main file or as a dynamically
|
|||
|
loaded file. See symbol_file_add_with_addrs's comments for details. */
|
|||
|
|
|||
|
struct objfile *
|
|||
|
symbol_file_add (const char *name, symfile_add_flags add_flags,
|
|||
|
section_addr_info *addrs, objfile_flags flags)
|
|||
|
{
|
|||
|
gdb_bfd_ref_ptr bfd (symfile_bfd_open (name));
|
|||
|
|
|||
|
return symbol_file_add_from_bfd (bfd.get (), name, add_flags, addrs,
|
|||
|
flags, NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* Call symbol_file_add() with default values and update whatever is
|
|||
|
affected by the loading of a new main().
|
|||
|
Used when the file is supplied in the gdb command line
|
|||
|
and by some targets with special loading requirements.
|
|||
|
The auxiliary function, symbol_file_add_main_1(), has the flags
|
|||
|
argument for the switches that can only be specified in the symbol_file
|
|||
|
command itself. */
|
|||
|
|
|||
|
void
|
|||
|
symbol_file_add_main (const char *args, symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
symbol_file_add_main_1 (args, add_flags, 0, 0);
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
symbol_file_add_main_1 (const char *args, symfile_add_flags add_flags,
|
|||
|
objfile_flags flags, CORE_ADDR reloff)
|
|||
|
{
|
|||
|
add_flags |= current_inferior ()->symfile_flags | SYMFILE_MAINLINE;
|
|||
|
|
|||
|
struct objfile *objfile = symbol_file_add (args, add_flags, NULL, flags);
|
|||
|
if (reloff != 0)
|
|||
|
objfile_rebase (objfile, reloff);
|
|||
|
|
|||
|
/* Getting new symbols may change our opinion about
|
|||
|
what is frameless. */
|
|||
|
reinit_frame_cache ();
|
|||
|
|
|||
|
if ((add_flags & SYMFILE_NO_READ) == 0)
|
|||
|
set_initial_language ();
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
symbol_file_clear (int from_tty)
|
|||
|
{
|
|||
|
if ((have_full_symbols () || have_partial_symbols ())
|
|||
|
&& from_tty
|
|||
|
&& (symfile_objfile
|
|||
|
? !query (_("Discard symbol table from `%s'? "),
|
|||
|
objfile_name (symfile_objfile))
|
|||
|
: !query (_("Discard symbol table? "))))
|
|||
|
error (_("Not confirmed."));
|
|||
|
|
|||
|
/* solib descriptors may have handles to objfiles. Wipe them before their
|
|||
|
objfiles get stale by free_all_objfiles. */
|
|||
|
no_shared_libraries (NULL, from_tty);
|
|||
|
|
|||
|
free_all_objfiles ();
|
|||
|
|
|||
|
gdb_assert (symfile_objfile == NULL);
|
|||
|
if (from_tty)
|
|||
|
printf_filtered (_("No symbol file now.\n"));
|
|||
|
}
|
|||
|
|
|||
|
/* See symfile.h. */
|
|||
|
|
|||
|
bool separate_debug_file_debug = false;
|
|||
|
|
|||
|
static int
|
|||
|
separate_debug_file_exists (const std::string &name, unsigned long crc,
|
|||
|
struct objfile *parent_objfile)
|
|||
|
{
|
|||
|
unsigned long file_crc;
|
|||
|
int file_crc_p;
|
|||
|
struct stat parent_stat, abfd_stat;
|
|||
|
int verified_as_different;
|
|||
|
|
|||
|
/* Find a separate debug info file as if symbols would be present in
|
|||
|
PARENT_OBJFILE itself this function would not be called. .gnu_debuglink
|
|||
|
section can contain just the basename of PARENT_OBJFILE without any
|
|||
|
".debug" suffix as "/usr/lib/debug/path/to/file" is a separate tree where
|
|||
|
the separate debug infos with the same basename can exist. */
|
|||
|
|
|||
|
if (filename_cmp (name.c_str (), objfile_name (parent_objfile)) == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (separate_debug_file_debug)
|
|||
|
{
|
|||
|
printf_filtered (_(" Trying %s..."), name.c_str ());
|
|||
|
gdb_flush (gdb_stdout);
|
|||
|
}
|
|||
|
|
|||
|
gdb_bfd_ref_ptr abfd (gdb_bfd_open (name.c_str (), gnutarget, -1));
|
|||
|
|
|||
|
if (abfd == NULL)
|
|||
|
{
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" no, unable to open.\n"));
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Verify symlinks were not the cause of filename_cmp name difference above.
|
|||
|
|
|||
|
Some operating systems, e.g. Windows, do not provide a meaningful
|
|||
|
st_ino; they always set it to zero. (Windows does provide a
|
|||
|
meaningful st_dev.) Files accessed from gdbservers that do not
|
|||
|
support the vFile:fstat packet will also have st_ino set to zero.
|
|||
|
Do not indicate a duplicate library in either case. While there
|
|||
|
is no guarantee that a system that provides meaningful inode
|
|||
|
numbers will never set st_ino to zero, this is merely an
|
|||
|
optimization, so we do not need to worry about false negatives. */
|
|||
|
|
|||
|
if (bfd_stat (abfd.get (), &abfd_stat) == 0
|
|||
|
&& abfd_stat.st_ino != 0
|
|||
|
&& bfd_stat (parent_objfile->obfd, &parent_stat) == 0)
|
|||
|
{
|
|||
|
if (abfd_stat.st_dev == parent_stat.st_dev
|
|||
|
&& abfd_stat.st_ino == parent_stat.st_ino)
|
|||
|
{
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" no, same file as the objfile.\n"));
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
verified_as_different = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
verified_as_different = 0;
|
|||
|
|
|||
|
file_crc_p = gdb_bfd_crc (abfd.get (), &file_crc);
|
|||
|
|
|||
|
if (!file_crc_p)
|
|||
|
{
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" no, error computing CRC.\n"));
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (crc != file_crc)
|
|||
|
{
|
|||
|
unsigned long parent_crc;
|
|||
|
|
|||
|
/* If the files could not be verified as different with
|
|||
|
bfd_stat then we need to calculate the parent's CRC
|
|||
|
to verify whether the files are different or not. */
|
|||
|
|
|||
|
if (!verified_as_different)
|
|||
|
{
|
|||
|
if (!gdb_bfd_crc (parent_objfile->obfd, &parent_crc))
|
|||
|
{
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" no, error computing CRC.\n"));
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (verified_as_different || parent_crc != file_crc)
|
|||
|
warning (_("the debug information found in \"%s\""
|
|||
|
" does not match \"%s\" (CRC mismatch).\n"),
|
|||
|
name.c_str (), objfile_name (parent_objfile));
|
|||
|
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" no, CRC doesn't match.\n"));
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_(" yes!\n"));
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
char *debug_file_directory = NULL;
|
|||
|
static void
|
|||
|
show_debug_file_directory (struct ui_file *file, int from_tty,
|
|||
|
struct cmd_list_element *c, const char *value)
|
|||
|
{
|
|||
|
fprintf_filtered (file,
|
|||
|
_("The directory where separate debug "
|
|||
|
"symbols are searched for is \"%s\".\n"),
|
|||
|
value);
|
|||
|
}
|
|||
|
|
|||
|
#if ! defined (DEBUG_SUBDIRECTORY)
|
|||
|
#define DEBUG_SUBDIRECTORY ".debug"
|
|||
|
#endif
|
|||
|
|
|||
|
/* Find a separate debuginfo file for OBJFILE, using DIR as the directory
|
|||
|
where the original file resides (may not be the same as
|
|||
|
dirname(objfile->name) due to symlinks), and DEBUGLINK as the file we are
|
|||
|
looking for. CANON_DIR is the "realpath" form of DIR.
|
|||
|
DIR must contain a trailing '/'.
|
|||
|
Returns the path of the file with separate debug info, or an empty
|
|||
|
string. */
|
|||
|
|
|||
|
static std::string
|
|||
|
find_separate_debug_file (const char *dir,
|
|||
|
const char *canon_dir,
|
|||
|
const char *debuglink,
|
|||
|
unsigned long crc32, struct objfile *objfile)
|
|||
|
{
|
|||
|
if (separate_debug_file_debug)
|
|||
|
printf_filtered (_("\nLooking for separate debug info (debug link) for "
|
|||
|
"%s\n"), objfile_name (objfile));
|
|||
|
|
|||
|
/* First try in the same directory as the original file. */
|
|||
|
std::string debugfile = dir;
|
|||
|
debugfile += debuglink;
|
|||
|
|
|||
|
if (separate_debug_file_exists (debugfile, crc32, objfile))
|
|||
|
return debugfile;
|
|||
|
|
|||
|
/* Then try in the subdirectory named DEBUG_SUBDIRECTORY. */
|
|||
|
debugfile = dir;
|
|||
|
debugfile += DEBUG_SUBDIRECTORY;
|
|||
|
debugfile += "/";
|
|||
|
debugfile += debuglink;
|
|||
|
|
|||
|
if (separate_debug_file_exists (debugfile, crc32, objfile))
|
|||
|
return debugfile;
|
|||
|
|
|||
|
/* Then try in the global debugfile directories.
|
|||
|
|
|||
|
Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
|
|||
|
cause "/..." lookups. */
|
|||
|
|
|||
|
bool target_prefix = startswith (dir, "target:");
|
|||
|
const char *dir_notarget = target_prefix ? dir + strlen ("target:") : dir;
|
|||
|
std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
|
|||
|
= dirnames_to_char_ptr_vec (debug_file_directory);
|
|||
|
gdb::unique_xmalloc_ptr<char> canon_sysroot = gdb_realpath (gdb_sysroot);
|
|||
|
|
|||
|
/* MS-Windows/MS-DOS don't allow colons in file names; we must
|
|||
|
convert the drive letter into a one-letter directory, so that the
|
|||
|
file name resulting from splicing below will be valid.
|
|||
|
|
|||
|
FIXME: The below only works when GDB runs on MS-Windows/MS-DOS.
|
|||
|
There are various remote-debugging scenarios where such a
|
|||
|
transformation of the drive letter might be required when GDB runs
|
|||
|
on a Posix host, see
|
|||
|
|
|||
|
https://sourceware.org/ml/gdb-patches/2019-04/msg00605.html
|
|||
|
|
|||
|
If some of those scenarios need to be supported, we will need to
|
|||
|
use a different condition for HAS_DRIVE_SPEC and a different macro
|
|||
|
instead of STRIP_DRIVE_SPEC, which work on Posix systems as well. */
|
|||
|
std::string drive;
|
|||
|
if (HAS_DRIVE_SPEC (dir_notarget))
|
|||
|
{
|
|||
|
drive = dir_notarget[0];
|
|||
|
dir_notarget = STRIP_DRIVE_SPEC (dir_notarget);
|
|||
|
}
|
|||
|
|
|||
|
for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec)
|
|||
|
{
|
|||
|
debugfile = target_prefix ? "target:" : "";
|
|||
|
debugfile += debugdir.get ();
|
|||
|
debugfile += "/";
|
|||
|
debugfile += drive;
|
|||
|
debugfile += dir_notarget;
|
|||
|
debugfile += debuglink;
|
|||
|
|
|||
|
if (separate_debug_file_exists (debugfile, crc32, objfile))
|
|||
|
return debugfile;
|
|||
|
|
|||
|
const char *base_path = NULL;
|
|||
|
if (canon_dir != NULL)
|
|||
|
{
|
|||
|
if (canon_sysroot.get () != NULL)
|
|||
|
base_path = child_path (canon_sysroot.get (), canon_dir);
|
|||
|
else
|
|||
|
base_path = child_path (gdb_sysroot, canon_dir);
|
|||
|
}
|
|||
|
if (base_path != NULL)
|
|||
|
{
|
|||
|
/* If the file is in the sysroot, try using its base path in
|
|||
|
the global debugfile directory. */
|
|||
|
debugfile = target_prefix ? "target:" : "";
|
|||
|
debugfile += debugdir.get ();
|
|||
|
debugfile += "/";
|
|||
|
debugfile += base_path;
|
|||
|
debugfile += "/";
|
|||
|
debugfile += debuglink;
|
|||
|
|
|||
|
if (separate_debug_file_exists (debugfile, crc32, objfile))
|
|||
|
return debugfile;
|
|||
|
|
|||
|
/* If the file is in the sysroot, try using its base path in
|
|||
|
the sysroot's global debugfile directory. */
|
|||
|
debugfile = target_prefix ? "target:" : "";
|
|||
|
debugfile += gdb_sysroot;
|
|||
|
debugfile += debugdir.get ();
|
|||
|
debugfile += "/";
|
|||
|
debugfile += base_path;
|
|||
|
debugfile += "/";
|
|||
|
debugfile += debuglink;
|
|||
|
|
|||
|
if (separate_debug_file_exists (debugfile, crc32, objfile))
|
|||
|
return debugfile;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return std::string ();
|
|||
|
}
|
|||
|
|
|||
|
/* Modify PATH to contain only "[/]directory/" part of PATH.
|
|||
|
If there were no directory separators in PATH, PATH will be empty
|
|||
|
string on return. */
|
|||
|
|
|||
|
static void
|
|||
|
terminate_after_last_dir_separator (char *path)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
/* Strip off the final filename part, leaving the directory name,
|
|||
|
followed by a slash. The directory can be relative or absolute. */
|
|||
|
for (i = strlen(path) - 1; i >= 0; i--)
|
|||
|
if (IS_DIR_SEPARATOR (path[i]))
|
|||
|
break;
|
|||
|
|
|||
|
/* If I is -1 then no directory is present there and DIR will be "". */
|
|||
|
path[i + 1] = '\0';
|
|||
|
}
|
|||
|
|
|||
|
/* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
|
|||
|
Returns pathname, or an empty string. */
|
|||
|
|
|||
|
std::string
|
|||
|
find_separate_debug_file_by_debuglink (struct objfile *objfile)
|
|||
|
{
|
|||
|
unsigned long crc32;
|
|||
|
|
|||
|
gdb::unique_xmalloc_ptr<char> debuglink
|
|||
|
(bfd_get_debug_link_info (objfile->obfd, &crc32));
|
|||
|
|
|||
|
if (debuglink == NULL)
|
|||
|
{
|
|||
|
/* There's no separate debug info, hence there's no way we could
|
|||
|
load it => no warning. */
|
|||
|
return std::string ();
|
|||
|
}
|
|||
|
|
|||
|
std::string dir = objfile_name (objfile);
|
|||
|
terminate_after_last_dir_separator (&dir[0]);
|
|||
|
gdb::unique_xmalloc_ptr<char> canon_dir (lrealpath (dir.c_str ()));
|
|||
|
|
|||
|
std::string debugfile
|
|||
|
= find_separate_debug_file (dir.c_str (), canon_dir.get (),
|
|||
|
debuglink.get (), crc32, objfile);
|
|||
|
|
|||
|
if (debugfile.empty ())
|
|||
|
{
|
|||
|
/* For PR gdb/9538, try again with realpath (if different from the
|
|||
|
original). */
|
|||
|
|
|||
|
struct stat st_buf;
|
|||
|
|
|||
|
if (lstat (objfile_name (objfile), &st_buf) == 0
|
|||
|
&& S_ISLNK (st_buf.st_mode))
|
|||
|
{
|
|||
|
gdb::unique_xmalloc_ptr<char> symlink_dir
|
|||
|
(lrealpath (objfile_name (objfile)));
|
|||
|
if (symlink_dir != NULL)
|
|||
|
{
|
|||
|
terminate_after_last_dir_separator (symlink_dir.get ());
|
|||
|
if (dir != symlink_dir.get ())
|
|||
|
{
|
|||
|
/* Different directory, so try using it. */
|
|||
|
debugfile = find_separate_debug_file (symlink_dir.get (),
|
|||
|
symlink_dir.get (),
|
|||
|
debuglink.get (),
|
|||
|
crc32,
|
|||
|
objfile);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return debugfile;
|
|||
|
}
|
|||
|
|
|||
|
/* Make sure that OBJF_{READNOW,READNEVER} are not set
|
|||
|
simultaneously. */
|
|||
|
|
|||
|
static void
|
|||
|
validate_readnow_readnever (objfile_flags flags)
|
|||
|
{
|
|||
|
if ((flags & OBJF_READNOW) && (flags & OBJF_READNEVER))
|
|||
|
error (_("-readnow and -readnever cannot be used simultaneously"));
|
|||
|
}
|
|||
|
|
|||
|
/* This is the symbol-file command. Read the file, analyze its
|
|||
|
symbols, and add a struct symtab to a symtab list. The syntax of
|
|||
|
the command is rather bizarre:
|
|||
|
|
|||
|
1. The function buildargv implements various quoting conventions
|
|||
|
which are undocumented and have little or nothing in common with
|
|||
|
the way things are quoted (or not quoted) elsewhere in GDB.
|
|||
|
|
|||
|
2. Options are used, which are not generally used in GDB (perhaps
|
|||
|
"set mapped on", "set readnow on" would be better)
|
|||
|
|
|||
|
3. The order of options matters, which is contrary to GNU
|
|||
|
conventions (because it is confusing and inconvenient). */
|
|||
|
|
|||
|
void
|
|||
|
symbol_file_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
dont_repeat ();
|
|||
|
|
|||
|
if (args == NULL)
|
|||
|
{
|
|||
|
symbol_file_clear (from_tty);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
objfile_flags flags = OBJF_USERLOADED;
|
|||
|
symfile_add_flags add_flags = 0;
|
|||
|
char *name = NULL;
|
|||
|
bool stop_processing_options = false;
|
|||
|
CORE_ADDR offset = 0;
|
|||
|
int idx;
|
|||
|
char *arg;
|
|||
|
|
|||
|
if (from_tty)
|
|||
|
add_flags |= SYMFILE_VERBOSE;
|
|||
|
|
|||
|
gdb_argv built_argv (args);
|
|||
|
for (arg = built_argv[0], idx = 0; arg != NULL; arg = built_argv[++idx])
|
|||
|
{
|
|||
|
if (stop_processing_options || *arg != '-')
|
|||
|
{
|
|||
|
if (name == NULL)
|
|||
|
name = arg;
|
|||
|
else
|
|||
|
error (_("Unrecognized argument \"%s\""), arg);
|
|||
|
}
|
|||
|
else if (strcmp (arg, "-readnow") == 0)
|
|||
|
flags |= OBJF_READNOW;
|
|||
|
else if (strcmp (arg, "-readnever") == 0)
|
|||
|
flags |= OBJF_READNEVER;
|
|||
|
else if (strcmp (arg, "-o") == 0)
|
|||
|
{
|
|||
|
arg = built_argv[++idx];
|
|||
|
if (arg == NULL)
|
|||
|
error (_("Missing argument to -o"));
|
|||
|
|
|||
|
offset = parse_and_eval_address (arg);
|
|||
|
}
|
|||
|
else if (strcmp (arg, "--") == 0)
|
|||
|
stop_processing_options = true;
|
|||
|
else
|
|||
|
error (_("Unrecognized argument \"%s\""), arg);
|
|||
|
}
|
|||
|
|
|||
|
if (name == NULL)
|
|||
|
error (_("no symbol file name was specified"));
|
|||
|
|
|||
|
validate_readnow_readnever (flags);
|
|||
|
|
|||
|
/* Set SYMFILE_DEFER_BP_RESET because the proper displacement for a PIE
|
|||
|
(Position Independent Executable) main symbol file will only be
|
|||
|
computed by the solib_create_inferior_hook below. Without it,
|
|||
|
breakpoint_re_set would fail to insert the breakpoints with the zero
|
|||
|
displacement. */
|
|||
|
add_flags |= SYMFILE_DEFER_BP_RESET;
|
|||
|
|
|||
|
symbol_file_add_main_1 (name, add_flags, flags, offset);
|
|||
|
|
|||
|
solib_create_inferior_hook (from_tty);
|
|||
|
|
|||
|
/* Now it's safe to re-add the breakpoints. */
|
|||
|
breakpoint_re_set ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Set the initial language.
|
|||
|
|
|||
|
FIXME: A better solution would be to record the language in the
|
|||
|
psymtab when reading partial symbols, and then use it (if known) to
|
|||
|
set the language. This would be a win for formats that encode the
|
|||
|
language in an easily discoverable place, such as DWARF. For
|
|||
|
stabs, we can jump through hoops looking for specially named
|
|||
|
symbols or try to intuit the language from the specific type of
|
|||
|
stabs we find, but we can't do that until later when we read in
|
|||
|
full symbols. */
|
|||
|
|
|||
|
void
|
|||
|
set_initial_language (void)
|
|||
|
{
|
|||
|
enum language lang = main_language ();
|
|||
|
|
|||
|
if (lang == language_unknown)
|
|||
|
{
|
|||
|
const char *name = main_name ();
|
|||
|
struct symbol *sym = lookup_symbol (name, NULL, VAR_DOMAIN, NULL).symbol;
|
|||
|
|
|||
|
if (sym != NULL)
|
|||
|
lang = SYMBOL_LANGUAGE (sym);
|
|||
|
}
|
|||
|
|
|||
|
if (lang == language_unknown)
|
|||
|
{
|
|||
|
/* Make C the default language */
|
|||
|
lang = language_c;
|
|||
|
}
|
|||
|
|
|||
|
set_language (lang);
|
|||
|
expected_language = current_language; /* Don't warn the user. */
|
|||
|
}
|
|||
|
|
|||
|
/* Open the file specified by NAME and hand it off to BFD for
|
|||
|
preliminary analysis. Return a newly initialized bfd *, which
|
|||
|
includes a newly malloc'd` copy of NAME (tilde-expanded and made
|
|||
|
absolute). In case of trouble, error() is called. */
|
|||
|
|
|||
|
gdb_bfd_ref_ptr
|
|||
|
symfile_bfd_open (const char *name)
|
|||
|
{
|
|||
|
int desc = -1;
|
|||
|
|
|||
|
gdb::unique_xmalloc_ptr<char> absolute_name;
|
|||
|
if (!is_target_filename (name))
|
|||
|
{
|
|||
|
gdb::unique_xmalloc_ptr<char> expanded_name (tilde_expand (name));
|
|||
|
|
|||
|
/* Look down path for it, allocate 2nd new malloc'd copy. */
|
|||
|
desc = openp (getenv ("PATH"),
|
|||
|
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
|
|||
|
expanded_name.get (), O_RDONLY | O_BINARY, &absolute_name);
|
|||
|
#if defined(__GO32__) || defined(_WIN32) || defined (__CYGWIN__)
|
|||
|
if (desc < 0)
|
|||
|
{
|
|||
|
char *exename = (char *) alloca (strlen (expanded_name.get ()) + 5);
|
|||
|
|
|||
|
strcat (strcpy (exename, expanded_name.get ()), ".exe");
|
|||
|
desc = openp (getenv ("PATH"),
|
|||
|
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH,
|
|||
|
exename, O_RDONLY | O_BINARY, &absolute_name);
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (desc < 0)
|
|||
|
perror_with_name (expanded_name.get ());
|
|||
|
|
|||
|
name = absolute_name.get ();
|
|||
|
}
|
|||
|
|
|||
|
gdb_bfd_ref_ptr sym_bfd (gdb_bfd_open (name, gnutarget, desc));
|
|||
|
if (sym_bfd == NULL)
|
|||
|
error (_("`%s': can't open to read symbols: %s."), name,
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
|
|||
|
if (!gdb_bfd_has_target_filename (sym_bfd.get ()))
|
|||
|
bfd_set_cacheable (sym_bfd.get (), 1);
|
|||
|
|
|||
|
if (!bfd_check_format (sym_bfd.get (), bfd_object))
|
|||
|
error (_("`%s': can't read symbols: %s."), name,
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
|
|||
|
return sym_bfd;
|
|||
|
}
|
|||
|
|
|||
|
/* Return the section index for SECTION_NAME on OBJFILE. Return -1 if
|
|||
|
the section was not found. */
|
|||
|
|
|||
|
int
|
|||
|
get_section_index (struct objfile *objfile, const char *section_name)
|
|||
|
{
|
|||
|
asection *sect = bfd_get_section_by_name (objfile->obfd, section_name);
|
|||
|
|
|||
|
if (sect)
|
|||
|
return sect->index;
|
|||
|
else
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/* Link SF into the global symtab_fns list.
|
|||
|
FLAVOUR is the file format that SF handles.
|
|||
|
Called on startup by the _initialize routine in each object file format
|
|||
|
reader, to register information about each format the reader is prepared
|
|||
|
to handle. */
|
|||
|
|
|||
|
void
|
|||
|
add_symtab_fns (enum bfd_flavour flavour, const struct sym_fns *sf)
|
|||
|
{
|
|||
|
symtab_fns.emplace_back (flavour, sf);
|
|||
|
}
|
|||
|
|
|||
|
/* Initialize OBJFILE to read symbols from its associated BFD. It
|
|||
|
either returns or calls error(). The result is an initialized
|
|||
|
struct sym_fns in the objfile structure, that contains cached
|
|||
|
information about the symbol file. */
|
|||
|
|
|||
|
static const struct sym_fns *
|
|||
|
find_sym_fns (bfd *abfd)
|
|||
|
{
|
|||
|
enum bfd_flavour our_flavour = bfd_get_flavour (abfd);
|
|||
|
|
|||
|
if (our_flavour == bfd_target_srec_flavour
|
|||
|
|| our_flavour == bfd_target_ihex_flavour
|
|||
|
|| our_flavour == bfd_target_tekhex_flavour)
|
|||
|
return NULL; /* No symbols. */
|
|||
|
|
|||
|
for (const registered_sym_fns &rsf : symtab_fns)
|
|||
|
if (our_flavour == rsf.sym_flavour)
|
|||
|
return rsf.sym_fns;
|
|||
|
|
|||
|
error (_("I'm sorry, Dave, I can't do that. Symbol format `%s' unknown."),
|
|||
|
bfd_get_target (abfd));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function runs the load command of our current target. */
|
|||
|
|
|||
|
static void
|
|||
|
load_command (const char *arg, int from_tty)
|
|||
|
{
|
|||
|
dont_repeat ();
|
|||
|
|
|||
|
/* The user might be reloading because the binary has changed. Take
|
|||
|
this opportunity to check. */
|
|||
|
reopen_exec_file ();
|
|||
|
reread_symbols ();
|
|||
|
|
|||
|
std::string temp;
|
|||
|
if (arg == NULL)
|
|||
|
{
|
|||
|
const char *parg, *prev;
|
|||
|
|
|||
|
arg = get_exec_file (1);
|
|||
|
|
|||
|
/* We may need to quote this string so buildargv can pull it
|
|||
|
apart. */
|
|||
|
prev = parg = arg;
|
|||
|
while ((parg = strpbrk (parg, "\\\"'\t ")))
|
|||
|
{
|
|||
|
temp.append (prev, parg - prev);
|
|||
|
prev = parg++;
|
|||
|
temp.push_back ('\\');
|
|||
|
}
|
|||
|
/* If we have not copied anything yet, then we didn't see a
|
|||
|
character to quote, and we can just leave ARG unchanged. */
|
|||
|
if (!temp.empty ())
|
|||
|
{
|
|||
|
temp.append (prev);
|
|||
|
arg = temp.c_str ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
target_load (arg, from_tty);
|
|||
|
|
|||
|
/* After re-loading the executable, we don't really know which
|
|||
|
overlays are mapped any more. */
|
|||
|
overlay_cache_invalid = 1;
|
|||
|
}
|
|||
|
|
|||
|
/* This version of "load" should be usable for any target. Currently
|
|||
|
it is just used for remote targets, not inftarg.c or core files,
|
|||
|
on the theory that only in that case is it useful.
|
|||
|
|
|||
|
Avoiding xmodem and the like seems like a win (a) because we don't have
|
|||
|
to worry about finding it, and (b) On VMS, fork() is very slow and so
|
|||
|
we don't want to run a subprocess. On the other hand, I'm not sure how
|
|||
|
performance compares. */
|
|||
|
|
|||
|
static int validate_download = 0;
|
|||
|
|
|||
|
/* Callback service function for generic_load (bfd_map_over_sections). */
|
|||
|
|
|||
|
static void
|
|||
|
add_section_size_callback (bfd *abfd, asection *asec, void *data)
|
|||
|
{
|
|||
|
bfd_size_type *sum = (bfd_size_type *) data;
|
|||
|
|
|||
|
*sum += bfd_section_size (asec);
|
|||
|
}
|
|||
|
|
|||
|
/* Opaque data for load_progress. */
|
|||
|
struct load_progress_data
|
|||
|
{
|
|||
|
/* Cumulative data. */
|
|||
|
unsigned long write_count = 0;
|
|||
|
unsigned long data_count = 0;
|
|||
|
bfd_size_type total_size = 0;
|
|||
|
};
|
|||
|
|
|||
|
/* Opaque data for load_progress for a single section. */
|
|||
|
struct load_progress_section_data
|
|||
|
{
|
|||
|
load_progress_section_data (load_progress_data *cumulative_,
|
|||
|
const char *section_name_, ULONGEST section_size_,
|
|||
|
CORE_ADDR lma_, gdb_byte *buffer_)
|
|||
|
: cumulative (cumulative_), section_name (section_name_),
|
|||
|
section_size (section_size_), lma (lma_), buffer (buffer_)
|
|||
|
{}
|
|||
|
|
|||
|
struct load_progress_data *cumulative;
|
|||
|
|
|||
|
/* Per-section data. */
|
|||
|
const char *section_name;
|
|||
|
ULONGEST section_sent = 0;
|
|||
|
ULONGEST section_size;
|
|||
|
CORE_ADDR lma;
|
|||
|
gdb_byte *buffer;
|
|||
|
};
|
|||
|
|
|||
|
/* Opaque data for load_section_callback. */
|
|||
|
struct load_section_data
|
|||
|
{
|
|||
|
load_section_data (load_progress_data *progress_data_)
|
|||
|
: progress_data (progress_data_)
|
|||
|
{}
|
|||
|
|
|||
|
~load_section_data ()
|
|||
|
{
|
|||
|
for (auto &&request : requests)
|
|||
|
{
|
|||
|
xfree (request.data);
|
|||
|
delete ((load_progress_section_data *) request.baton);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CORE_ADDR load_offset = 0;
|
|||
|
struct load_progress_data *progress_data;
|
|||
|
std::vector<struct memory_write_request> requests;
|
|||
|
};
|
|||
|
|
|||
|
/* Target write callback routine for progress reporting. */
|
|||
|
|
|||
|
static void
|
|||
|
load_progress (ULONGEST bytes, void *untyped_arg)
|
|||
|
{
|
|||
|
struct load_progress_section_data *args
|
|||
|
= (struct load_progress_section_data *) untyped_arg;
|
|||
|
struct load_progress_data *totals;
|
|||
|
|
|||
|
if (args == NULL)
|
|||
|
/* Writing padding data. No easy way to get at the cumulative
|
|||
|
stats, so just ignore this. */
|
|||
|
return;
|
|||
|
|
|||
|
totals = args->cumulative;
|
|||
|
|
|||
|
if (bytes == 0 && args->section_sent == 0)
|
|||
|
{
|
|||
|
/* The write is just starting. Let the user know we've started
|
|||
|
this section. */
|
|||
|
current_uiout->message ("Loading section %s, size %s lma %s\n",
|
|||
|
args->section_name,
|
|||
|
hex_string (args->section_size),
|
|||
|
paddress (target_gdbarch (), args->lma));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (validate_download)
|
|||
|
{
|
|||
|
/* Broken memories and broken monitors manifest themselves here
|
|||
|
when bring new computers to life. This doubles already slow
|
|||
|
downloads. */
|
|||
|
/* NOTE: cagney/1999-10-18: A more efficient implementation
|
|||
|
might add a verify_memory() method to the target vector and
|
|||
|
then use that. remote.c could implement that method using
|
|||
|
the ``qCRC'' packet. */
|
|||
|
gdb::byte_vector check (bytes);
|
|||
|
|
|||
|
if (target_read_memory (args->lma, check.data (), bytes) != 0)
|
|||
|
error (_("Download verify read failed at %s"),
|
|||
|
paddress (target_gdbarch (), args->lma));
|
|||
|
if (memcmp (args->buffer, check.data (), bytes) != 0)
|
|||
|
error (_("Download verify compare failed at %s"),
|
|||
|
paddress (target_gdbarch (), args->lma));
|
|||
|
}
|
|||
|
totals->data_count += bytes;
|
|||
|
args->lma += bytes;
|
|||
|
args->buffer += bytes;
|
|||
|
totals->write_count += 1;
|
|||
|
args->section_sent += bytes;
|
|||
|
if (check_quit_flag ()
|
|||
|
|| (deprecated_ui_load_progress_hook != NULL
|
|||
|
&& deprecated_ui_load_progress_hook (args->section_name,
|
|||
|
args->section_sent)))
|
|||
|
error (_("Canceled the download"));
|
|||
|
|
|||
|
if (deprecated_show_load_progress != NULL)
|
|||
|
deprecated_show_load_progress (args->section_name,
|
|||
|
args->section_sent,
|
|||
|
args->section_size,
|
|||
|
totals->data_count,
|
|||
|
totals->total_size);
|
|||
|
}
|
|||
|
|
|||
|
/* Callback service function for generic_load (bfd_map_over_sections). */
|
|||
|
|
|||
|
static void
|
|||
|
load_section_callback (bfd *abfd, asection *asec, void *data)
|
|||
|
{
|
|||
|
struct load_section_data *args = (struct load_section_data *) data;
|
|||
|
bfd_size_type size = bfd_section_size (asec);
|
|||
|
const char *sect_name = bfd_section_name (asec);
|
|||
|
|
|||
|
if ((bfd_section_flags (asec) & SEC_LOAD) == 0)
|
|||
|
return;
|
|||
|
|
|||
|
if (size == 0)
|
|||
|
return;
|
|||
|
|
|||
|
ULONGEST begin = bfd_section_lma (asec) + args->load_offset;
|
|||
|
ULONGEST end = begin + size;
|
|||
|
gdb_byte *buffer = (gdb_byte *) xmalloc (size);
|
|||
|
bfd_get_section_contents (abfd, asec, buffer, 0, size);
|
|||
|
|
|||
|
load_progress_section_data *section_data
|
|||
|
= new load_progress_section_data (args->progress_data, sect_name, size,
|
|||
|
begin, buffer);
|
|||
|
|
|||
|
args->requests.emplace_back (begin, end, buffer, section_data);
|
|||
|
}
|
|||
|
|
|||
|
static void print_transfer_performance (struct ui_file *stream,
|
|||
|
unsigned long data_count,
|
|||
|
unsigned long write_count,
|
|||
|
std::chrono::steady_clock::duration d);
|
|||
|
|
|||
|
/* See symfile.h. */
|
|||
|
|
|||
|
void
|
|||
|
generic_load (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct load_progress_data total_progress;
|
|||
|
struct load_section_data cbdata (&total_progress);
|
|||
|
struct ui_out *uiout = current_uiout;
|
|||
|
|
|||
|
if (args == NULL)
|
|||
|
error_no_arg (_("file to load"));
|
|||
|
|
|||
|
gdb_argv argv (args);
|
|||
|
|
|||
|
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (argv[0]));
|
|||
|
|
|||
|
if (argv[1] != NULL)
|
|||
|
{
|
|||
|
const char *endptr;
|
|||
|
|
|||
|
cbdata.load_offset = strtoulst (argv[1], &endptr, 0);
|
|||
|
|
|||
|
/* If the last word was not a valid number then
|
|||
|
treat it as a file name with spaces in. */
|
|||
|
if (argv[1] == endptr)
|
|||
|
error (_("Invalid download offset:%s."), argv[1]);
|
|||
|
|
|||
|
if (argv[2] != NULL)
|
|||
|
error (_("Too many parameters."));
|
|||
|
}
|
|||
|
|
|||
|
/* Open the file for loading. */
|
|||
|
gdb_bfd_ref_ptr loadfile_bfd (gdb_bfd_open (filename.get (), gnutarget, -1));
|
|||
|
if (loadfile_bfd == NULL)
|
|||
|
perror_with_name (filename.get ());
|
|||
|
|
|||
|
if (!bfd_check_format (loadfile_bfd.get (), bfd_object))
|
|||
|
{
|
|||
|
error (_("\"%s\" is not an object file: %s"), filename.get (),
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
}
|
|||
|
|
|||
|
bfd_map_over_sections (loadfile_bfd.get (), add_section_size_callback,
|
|||
|
(void *) &total_progress.total_size);
|
|||
|
|
|||
|
bfd_map_over_sections (loadfile_bfd.get (), load_section_callback, &cbdata);
|
|||
|
|
|||
|
using namespace std::chrono;
|
|||
|
|
|||
|
steady_clock::time_point start_time = steady_clock::now ();
|
|||
|
|
|||
|
if (target_write_memory_blocks (cbdata.requests, flash_discard,
|
|||
|
load_progress) != 0)
|
|||
|
error (_("Load failed"));
|
|||
|
|
|||
|
steady_clock::time_point end_time = steady_clock::now ();
|
|||
|
|
|||
|
CORE_ADDR entry = bfd_get_start_address (loadfile_bfd.get ());
|
|||
|
entry = gdbarch_addr_bits_remove (target_gdbarch (), entry);
|
|||
|
uiout->text ("Start address ");
|
|||
|
uiout->field_core_addr ("address", target_gdbarch (), entry);
|
|||
|
uiout->text (", load size ");
|
|||
|
uiout->field_unsigned ("load-size", total_progress.data_count);
|
|||
|
uiout->text ("\n");
|
|||
|
regcache_write_pc (get_current_regcache (), entry);
|
|||
|
|
|||
|
/* Reset breakpoints, now that we have changed the load image. For
|
|||
|
instance, breakpoints may have been set (or reset, by
|
|||
|
post_create_inferior) while connected to the target but before we
|
|||
|
loaded the program. In that case, the prologue analyzer could
|
|||
|
have read instructions from the target to find the right
|
|||
|
breakpoint locations. Loading has changed the contents of that
|
|||
|
memory. */
|
|||
|
|
|||
|
breakpoint_re_set ();
|
|||
|
|
|||
|
print_transfer_performance (gdb_stdout, total_progress.data_count,
|
|||
|
total_progress.write_count,
|
|||
|
end_time - start_time);
|
|||
|
}
|
|||
|
|
|||
|
/* Report on STREAM the performance of a memory transfer operation,
|
|||
|
such as 'load'. DATA_COUNT is the number of bytes transferred.
|
|||
|
WRITE_COUNT is the number of separate write operations, or 0, if
|
|||
|
that information is not available. TIME is how long the operation
|
|||
|
lasted. */
|
|||
|
|
|||
|
static void
|
|||
|
print_transfer_performance (struct ui_file *stream,
|
|||
|
unsigned long data_count,
|
|||
|
unsigned long write_count,
|
|||
|
std::chrono::steady_clock::duration time)
|
|||
|
{
|
|||
|
using namespace std::chrono;
|
|||
|
struct ui_out *uiout = current_uiout;
|
|||
|
|
|||
|
milliseconds ms = duration_cast<milliseconds> (time);
|
|||
|
|
|||
|
uiout->text ("Transfer rate: ");
|
|||
|
if (ms.count () > 0)
|
|||
|
{
|
|||
|
unsigned long rate = ((ULONGEST) data_count * 1000) / ms.count ();
|
|||
|
|
|||
|
if (uiout->is_mi_like_p ())
|
|||
|
{
|
|||
|
uiout->field_unsigned ("transfer-rate", rate * 8);
|
|||
|
uiout->text (" bits/sec");
|
|||
|
}
|
|||
|
else if (rate < 1024)
|
|||
|
{
|
|||
|
uiout->field_unsigned ("transfer-rate", rate);
|
|||
|
uiout->text (" bytes/sec");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
uiout->field_unsigned ("transfer-rate", rate / 1024);
|
|||
|
uiout->text (" KB/sec");
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
uiout->field_unsigned ("transferred-bits", (data_count * 8));
|
|||
|
uiout->text (" bits in <1 sec");
|
|||
|
}
|
|||
|
if (write_count > 0)
|
|||
|
{
|
|||
|
uiout->text (", ");
|
|||
|
uiout->field_unsigned ("write-rate", data_count / write_count);
|
|||
|
uiout->text (" bytes/write");
|
|||
|
}
|
|||
|
uiout->text (".\n");
|
|||
|
}
|
|||
|
|
|||
|
/* Add an OFFSET to the start address of each section in OBJF, except
|
|||
|
sections that were specified in ADDRS. */
|
|||
|
|
|||
|
static void
|
|||
|
set_objfile_default_section_offset (struct objfile *objf,
|
|||
|
const section_addr_info &addrs,
|
|||
|
CORE_ADDR offset)
|
|||
|
{
|
|||
|
/* Add OFFSET to all sections by default. */
|
|||
|
std::vector<struct section_offsets> offsets (objf->num_sections,
|
|||
|
{ { offset } });
|
|||
|
|
|||
|
/* Create sorted lists of all sections in ADDRS as well as all
|
|||
|
sections in OBJF. */
|
|||
|
|
|||
|
std::vector<const struct other_sections *> addrs_sorted
|
|||
|
= addrs_section_sort (addrs);
|
|||
|
|
|||
|
section_addr_info objf_addrs
|
|||
|
= build_section_addr_info_from_objfile (objf);
|
|||
|
std::vector<const struct other_sections *> objf_addrs_sorted
|
|||
|
= addrs_section_sort (objf_addrs);
|
|||
|
|
|||
|
/* Walk the BFD section list, and if a matching section is found in
|
|||
|
ADDRS_SORTED_LIST, set its offset to zero to keep its address
|
|||
|
unchanged.
|
|||
|
|
|||
|
Note that both lists may contain multiple sections with the same
|
|||
|
name, and then the sections from ADDRS are matched in BFD order
|
|||
|
(thanks to sectindex). */
|
|||
|
|
|||
|
std::vector<const struct other_sections *>::iterator addrs_sorted_iter
|
|||
|
= addrs_sorted.begin ();
|
|||
|
for (const other_sections *objf_sect : objf_addrs_sorted)
|
|||
|
{
|
|||
|
const char *objf_name = addr_section_name (objf_sect->name.c_str ());
|
|||
|
int cmp = -1;
|
|||
|
|
|||
|
while (cmp < 0 && addrs_sorted_iter != addrs_sorted.end ())
|
|||
|
{
|
|||
|
const struct other_sections *sect = *addrs_sorted_iter;
|
|||
|
const char *sect_name = addr_section_name (sect->name.c_str ());
|
|||
|
cmp = strcmp (sect_name, objf_name);
|
|||
|
if (cmp <= 0)
|
|||
|
++addrs_sorted_iter;
|
|||
|
}
|
|||
|
|
|||
|
if (cmp == 0)
|
|||
|
offsets[objf_sect->sectindex].offsets[0] = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Apply the new section offsets. */
|
|||
|
objfile_relocate (objf, offsets.data ());
|
|||
|
}
|
|||
|
|
|||
|
/* This function allows the addition of incrementally linked object files.
|
|||
|
It does not modify any state in the target, only in the debugger. */
|
|||
|
|
|||
|
static void
|
|||
|
add_symbol_file_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct gdbarch *gdbarch = get_current_arch ();
|
|||
|
gdb::unique_xmalloc_ptr<char> filename;
|
|||
|
char *arg;
|
|||
|
int argcnt = 0;
|
|||
|
struct objfile *objf;
|
|||
|
objfile_flags flags = OBJF_USERLOADED | OBJF_SHARED;
|
|||
|
symfile_add_flags add_flags = 0;
|
|||
|
|
|||
|
if (from_tty)
|
|||
|
add_flags |= SYMFILE_VERBOSE;
|
|||
|
|
|||
|
struct sect_opt
|
|||
|
{
|
|||
|
const char *name;
|
|||
|
const char *value;
|
|||
|
};
|
|||
|
|
|||
|
std::vector<sect_opt> sect_opts = { { ".text", NULL } };
|
|||
|
bool stop_processing_options = false;
|
|||
|
CORE_ADDR offset = 0;
|
|||
|
|
|||
|
dont_repeat ();
|
|||
|
|
|||
|
if (args == NULL)
|
|||
|
error (_("add-symbol-file takes a file name and an address"));
|
|||
|
|
|||
|
bool seen_addr = false;
|
|||
|
bool seen_offset = false;
|
|||
|
gdb_argv argv (args);
|
|||
|
|
|||
|
for (arg = argv[0], argcnt = 0; arg != NULL; arg = argv[++argcnt])
|
|||
|
{
|
|||
|
if (stop_processing_options || *arg != '-')
|
|||
|
{
|
|||
|
if (filename == NULL)
|
|||
|
{
|
|||
|
/* First non-option argument is always the filename. */
|
|||
|
filename.reset (tilde_expand (arg));
|
|||
|
}
|
|||
|
else if (!seen_addr)
|
|||
|
{
|
|||
|
/* The second non-option argument is always the text
|
|||
|
address at which to load the program. */
|
|||
|
sect_opts[0].value = arg;
|
|||
|
seen_addr = true;
|
|||
|
}
|
|||
|
else
|
|||
|
error (_("Unrecognized argument \"%s\""), arg);
|
|||
|
}
|
|||
|
else if (strcmp (arg, "-readnow") == 0)
|
|||
|
flags |= OBJF_READNOW;
|
|||
|
else if (strcmp (arg, "-readnever") == 0)
|
|||
|
flags |= OBJF_READNEVER;
|
|||
|
else if (strcmp (arg, "-s") == 0)
|
|||
|
{
|
|||
|
if (argv[argcnt + 1] == NULL)
|
|||
|
error (_("Missing section name after \"-s\""));
|
|||
|
else if (argv[argcnt + 2] == NULL)
|
|||
|
error (_("Missing section address after \"-s\""));
|
|||
|
|
|||
|
sect_opt sect = { argv[argcnt + 1], argv[argcnt + 2] };
|
|||
|
|
|||
|
sect_opts.push_back (sect);
|
|||
|
argcnt += 2;
|
|||
|
}
|
|||
|
else if (strcmp (arg, "-o") == 0)
|
|||
|
{
|
|||
|
arg = argv[++argcnt];
|
|||
|
if (arg == NULL)
|
|||
|
error (_("Missing argument to -o"));
|
|||
|
|
|||
|
offset = parse_and_eval_address (arg);
|
|||
|
seen_offset = true;
|
|||
|
}
|
|||
|
else if (strcmp (arg, "--") == 0)
|
|||
|
stop_processing_options = true;
|
|||
|
else
|
|||
|
error (_("Unrecognized argument \"%s\""), arg);
|
|||
|
}
|
|||
|
|
|||
|
if (filename == NULL)
|
|||
|
error (_("You must provide a filename to be loaded."));
|
|||
|
|
|||
|
validate_readnow_readnever (flags);
|
|||
|
|
|||
|
/* Print the prompt for the query below. And save the arguments into
|
|||
|
a sect_addr_info structure to be passed around to other
|
|||
|
functions. We have to split this up into separate print
|
|||
|
statements because hex_string returns a local static
|
|||
|
string. */
|
|||
|
|
|||
|
printf_unfiltered (_("add symbol table from file \"%s\""),
|
|||
|
filename.get ());
|
|||
|
section_addr_info section_addrs;
|
|||
|
std::vector<sect_opt>::const_iterator it = sect_opts.begin ();
|
|||
|
if (!seen_addr)
|
|||
|
++it;
|
|||
|
for (; it != sect_opts.end (); ++it)
|
|||
|
{
|
|||
|
CORE_ADDR addr;
|
|||
|
const char *val = it->value;
|
|||
|
const char *sec = it->name;
|
|||
|
|
|||
|
if (section_addrs.empty ())
|
|||
|
printf_unfiltered (_(" at\n"));
|
|||
|
addr = parse_and_eval_address (val);
|
|||
|
|
|||
|
/* Here we store the section offsets in the order they were
|
|||
|
entered on the command line. Every array element is
|
|||
|
assigned an ascending section index to preserve the above
|
|||
|
order over an unstable sorting algorithm. This dummy
|
|||
|
index is not used for any other purpose.
|
|||
|
*/
|
|||
|
section_addrs.emplace_back (addr, sec, section_addrs.size ());
|
|||
|
printf_filtered ("\t%s_addr = %s\n", sec,
|
|||
|
paddress (gdbarch, addr));
|
|||
|
|
|||
|
/* The object's sections are initialized when a
|
|||
|
call is made to build_objfile_section_table (objfile).
|
|||
|
This happens in reread_symbols.
|
|||
|
At this point, we don't know what file type this is,
|
|||
|
so we can't determine what section names are valid. */
|
|||
|
}
|
|||
|
if (seen_offset)
|
|||
|
printf_unfiltered (_("%s offset by %s\n"),
|
|||
|
(section_addrs.empty ()
|
|||
|
? _(" with all sections")
|
|||
|
: _("with other sections")),
|
|||
|
paddress (gdbarch, offset));
|
|||
|
else if (section_addrs.empty ())
|
|||
|
printf_unfiltered ("\n");
|
|||
|
|
|||
|
if (from_tty && (!query ("%s", "")))
|
|||
|
error (_("Not confirmed."));
|
|||
|
|
|||
|
objf = symbol_file_add (filename.get (), add_flags, §ion_addrs,
|
|||
|
flags);
|
|||
|
if (!objfile_has_symbols (objf) && objf->per_bfd->minimal_symbol_count <= 0)
|
|||
|
warning (_("newly-added symbol file \"%s\" does not provide any symbols"),
|
|||
|
filename.get ());
|
|||
|
|
|||
|
if (seen_offset)
|
|||
|
set_objfile_default_section_offset (objf, section_addrs, offset);
|
|||
|
|
|||
|
add_target_sections_of_objfile (objf);
|
|||
|
|
|||
|
/* Getting new symbols may change our opinion about what is
|
|||
|
frameless. */
|
|||
|
reinit_frame_cache ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function removes a symbol file that was added via add-symbol-file. */
|
|||
|
|
|||
|
static void
|
|||
|
remove_symbol_file_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct objfile *objf = NULL;
|
|||
|
struct program_space *pspace = current_program_space;
|
|||
|
|
|||
|
dont_repeat ();
|
|||
|
|
|||
|
if (args == NULL)
|
|||
|
error (_("remove-symbol-file: no symbol file provided"));
|
|||
|
|
|||
|
gdb_argv argv (args);
|
|||
|
|
|||
|
if (strcmp (argv[0], "-a") == 0)
|
|||
|
{
|
|||
|
/* Interpret the next argument as an address. */
|
|||
|
CORE_ADDR addr;
|
|||
|
|
|||
|
if (argv[1] == NULL)
|
|||
|
error (_("Missing address argument"));
|
|||
|
|
|||
|
if (argv[2] != NULL)
|
|||
|
error (_("Junk after %s"), argv[1]);
|
|||
|
|
|||
|
addr = parse_and_eval_address (argv[1]);
|
|||
|
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
{
|
|||
|
if ((objfile->flags & OBJF_USERLOADED) != 0
|
|||
|
&& (objfile->flags & OBJF_SHARED) != 0
|
|||
|
&& objfile->pspace == pspace
|
|||
|
&& is_addr_in_objfile (addr, objfile))
|
|||
|
{
|
|||
|
objf = objfile;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else if (argv[0] != NULL)
|
|||
|
{
|
|||
|
/* Interpret the current argument as a file name. */
|
|||
|
|
|||
|
if (argv[1] != NULL)
|
|||
|
error (_("Junk after %s"), argv[0]);
|
|||
|
|
|||
|
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (argv[0]));
|
|||
|
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
{
|
|||
|
if ((objfile->flags & OBJF_USERLOADED) != 0
|
|||
|
&& (objfile->flags & OBJF_SHARED) != 0
|
|||
|
&& objfile->pspace == pspace
|
|||
|
&& filename_cmp (filename.get (), objfile_name (objfile)) == 0)
|
|||
|
{
|
|||
|
objf = objfile;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (objf == NULL)
|
|||
|
error (_("No symbol file found"));
|
|||
|
|
|||
|
if (from_tty
|
|||
|
&& !query (_("Remove symbol table from file \"%s\"? "),
|
|||
|
objfile_name (objf)))
|
|||
|
error (_("Not confirmed."));
|
|||
|
|
|||
|
delete objf;
|
|||
|
clear_symtab_users (0);
|
|||
|
}
|
|||
|
|
|||
|
/* Re-read symbols if a symbol-file has changed. */
|
|||
|
|
|||
|
void
|
|||
|
reread_symbols (void)
|
|||
|
{
|
|||
|
long new_modtime;
|
|||
|
struct stat new_statbuf;
|
|||
|
int res;
|
|||
|
std::vector<struct objfile *> new_objfiles;
|
|||
|
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
{
|
|||
|
if (objfile->obfd == NULL)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Separate debug objfiles are handled in the main objfile. */
|
|||
|
if (objfile->separate_debug_objfile_backlink)
|
|||
|
continue;
|
|||
|
|
|||
|
/* If this object is from an archive (what you usually create with
|
|||
|
`ar', often called a `static library' on most systems, though
|
|||
|
a `shared library' on AIX is also an archive), then you should
|
|||
|
stat on the archive name, not member name. */
|
|||
|
if (objfile->obfd->my_archive)
|
|||
|
res = stat (objfile->obfd->my_archive->filename, &new_statbuf);
|
|||
|
else
|
|||
|
res = stat (objfile_name (objfile), &new_statbuf);
|
|||
|
if (res != 0)
|
|||
|
{
|
|||
|
/* FIXME, should use print_sys_errmsg but it's not filtered. */
|
|||
|
printf_filtered (_("`%s' has disappeared; keeping its symbols.\n"),
|
|||
|
objfile_name (objfile));
|
|||
|
continue;
|
|||
|
}
|
|||
|
new_modtime = new_statbuf.st_mtime;
|
|||
|
if (new_modtime != objfile->mtime)
|
|||
|
{
|
|||
|
struct section_offsets *offsets;
|
|||
|
int num_offsets;
|
|||
|
|
|||
|
printf_filtered (_("`%s' has changed; re-reading symbols.\n"),
|
|||
|
objfile_name (objfile));
|
|||
|
|
|||
|
/* There are various functions like symbol_file_add,
|
|||
|
symfile_bfd_open, syms_from_objfile, etc., which might
|
|||
|
appear to do what we want. But they have various other
|
|||
|
effects which we *don't* want. So we just do stuff
|
|||
|
ourselves. We don't worry about mapped files (for one thing,
|
|||
|
any mapped file will be out of date). */
|
|||
|
|
|||
|
/* If we get an error, blow away this objfile (not sure if
|
|||
|
that is the correct response for things like shared
|
|||
|
libraries). */
|
|||
|
std::unique_ptr<struct objfile> objfile_holder (objfile);
|
|||
|
|
|||
|
/* We need to do this whenever any symbols go away. */
|
|||
|
clear_symtab_users_cleanup defer_clear_users (0);
|
|||
|
|
|||
|
if (exec_bfd != NULL
|
|||
|
&& filename_cmp (bfd_get_filename (objfile->obfd),
|
|||
|
bfd_get_filename (exec_bfd)) == 0)
|
|||
|
{
|
|||
|
/* Reload EXEC_BFD without asking anything. */
|
|||
|
|
|||
|
exec_file_attach (bfd_get_filename (objfile->obfd), 0);
|
|||
|
}
|
|||
|
|
|||
|
/* Keep the calls order approx. the same as in free_objfile. */
|
|||
|
|
|||
|
/* Free the separate debug objfiles. It will be
|
|||
|
automatically recreated by sym_read. */
|
|||
|
free_objfile_separate_debug (objfile);
|
|||
|
|
|||
|
/* Clear the stale source cache. */
|
|||
|
forget_cached_source_info ();
|
|||
|
|
|||
|
/* Remove any references to this objfile in the global
|
|||
|
value lists. */
|
|||
|
preserve_values (objfile);
|
|||
|
|
|||
|
/* Nuke all the state that we will re-read. Much of the following
|
|||
|
code which sets things to NULL really is necessary to tell
|
|||
|
other parts of GDB that there is nothing currently there.
|
|||
|
|
|||
|
Try to keep the freeing order compatible with free_objfile. */
|
|||
|
|
|||
|
if (objfile->sf != NULL)
|
|||
|
{
|
|||
|
(*objfile->sf->sym_finish) (objfile);
|
|||
|
}
|
|||
|
|
|||
|
clear_objfile_data (objfile);
|
|||
|
|
|||
|
/* Clean up any state BFD has sitting around. */
|
|||
|
{
|
|||
|
gdb_bfd_ref_ptr obfd (objfile->obfd);
|
|||
|
const char *obfd_filename;
|
|||
|
|
|||
|
obfd_filename = bfd_get_filename (objfile->obfd);
|
|||
|
/* Open the new BFD before freeing the old one, so that
|
|||
|
the filename remains live. */
|
|||
|
gdb_bfd_ref_ptr temp (gdb_bfd_open (obfd_filename, gnutarget, -1));
|
|||
|
objfile->obfd = temp.release ();
|
|||
|
if (objfile->obfd == NULL)
|
|||
|
error (_("Can't open %s to read symbols."), obfd_filename);
|
|||
|
}
|
|||
|
|
|||
|
std::string original_name = objfile->original_name;
|
|||
|
|
|||
|
/* bfd_openr sets cacheable to true, which is what we want. */
|
|||
|
if (!bfd_check_format (objfile->obfd, bfd_object))
|
|||
|
error (_("Can't read symbols from %s: %s."), objfile_name (objfile),
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
|
|||
|
/* Save the offsets, we will nuke them with the rest of the
|
|||
|
objfile_obstack. */
|
|||
|
num_offsets = objfile->num_sections;
|
|||
|
offsets = ((struct section_offsets *)
|
|||
|
alloca (SIZEOF_N_SECTION_OFFSETS (num_offsets)));
|
|||
|
memcpy (offsets, objfile->section_offsets,
|
|||
|
SIZEOF_N_SECTION_OFFSETS (num_offsets));
|
|||
|
|
|||
|
objfile->reset_psymtabs ();
|
|||
|
|
|||
|
/* NB: after this call to obstack_free, objfiles_changed
|
|||
|
will need to be called (see discussion below). */
|
|||
|
obstack_free (&objfile->objfile_obstack, 0);
|
|||
|
objfile->sections = NULL;
|
|||
|
objfile->compunit_symtabs = NULL;
|
|||
|
objfile->template_symbols = NULL;
|
|||
|
objfile->static_links.reset (nullptr);
|
|||
|
|
|||
|
/* obstack_init also initializes the obstack so it is
|
|||
|
empty. We could use obstack_specify_allocation but
|
|||
|
gdb_obstack.h specifies the alloc/dealloc functions. */
|
|||
|
obstack_init (&objfile->objfile_obstack);
|
|||
|
|
|||
|
/* set_objfile_per_bfd potentially allocates the per-bfd
|
|||
|
data on the objfile's obstack (if sharing data across
|
|||
|
multiple users is not possible), so it's important to
|
|||
|
do it *after* the obstack has been initialized. */
|
|||
|
set_objfile_per_bfd (objfile);
|
|||
|
|
|||
|
objfile->original_name
|
|||
|
= obstack_strdup (&objfile->objfile_obstack, original_name);
|
|||
|
|
|||
|
/* Reset the sym_fns pointer. The ELF reader can change it
|
|||
|
based on whether .gdb_index is present, and we need it to
|
|||
|
start over. PR symtab/15885 */
|
|||
|
objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd));
|
|||
|
|
|||
|
build_objfile_section_table (objfile);
|
|||
|
|
|||
|
/* We use the same section offsets as from last time. I'm not
|
|||
|
sure whether that is always correct for shared libraries. */
|
|||
|
objfile->section_offsets = (struct section_offsets *)
|
|||
|
obstack_alloc (&objfile->objfile_obstack,
|
|||
|
SIZEOF_N_SECTION_OFFSETS (num_offsets));
|
|||
|
memcpy (objfile->section_offsets, offsets,
|
|||
|
SIZEOF_N_SECTION_OFFSETS (num_offsets));
|
|||
|
objfile->num_sections = num_offsets;
|
|||
|
|
|||
|
/* What the hell is sym_new_init for, anyway? The concept of
|
|||
|
distinguishing between the main file and additional files
|
|||
|
in this way seems rather dubious. */
|
|||
|
if (objfile == symfile_objfile)
|
|||
|
{
|
|||
|
(*objfile->sf->sym_new_init) (objfile);
|
|||
|
}
|
|||
|
|
|||
|
(*objfile->sf->sym_init) (objfile);
|
|||
|
clear_complaints ();
|
|||
|
|
|||
|
objfile->flags &= ~OBJF_PSYMTABS_READ;
|
|||
|
|
|||
|
/* We are about to read new symbols and potentially also
|
|||
|
DWARF information. Some targets may want to pass addresses
|
|||
|
read from DWARF DIE's through an adjustment function before
|
|||
|
saving them, like MIPS, which may call into
|
|||
|
"find_pc_section". When called, that function will make
|
|||
|
use of per-objfile program space data.
|
|||
|
|
|||
|
Since we discarded our section information above, we have
|
|||
|
dangling pointers in the per-objfile program space data
|
|||
|
structure. Force GDB to update the section mapping
|
|||
|
information by letting it know the objfile has changed,
|
|||
|
making the dangling pointers point to correct data
|
|||
|
again. */
|
|||
|
|
|||
|
objfiles_changed ();
|
|||
|
|
|||
|
read_symbols (objfile, 0);
|
|||
|
|
|||
|
if (!objfile_has_symbols (objfile))
|
|||
|
{
|
|||
|
wrap_here ("");
|
|||
|
printf_filtered (_("(no debugging symbols found)\n"));
|
|||
|
wrap_here ("");
|
|||
|
}
|
|||
|
|
|||
|
/* We're done reading the symbol file; finish off complaints. */
|
|||
|
clear_complaints ();
|
|||
|
|
|||
|
/* Getting new symbols may change our opinion about what is
|
|||
|
frameless. */
|
|||
|
|
|||
|
reinit_frame_cache ();
|
|||
|
|
|||
|
/* Discard cleanups as symbol reading was successful. */
|
|||
|
objfile_holder.release ();
|
|||
|
defer_clear_users.release ();
|
|||
|
|
|||
|
/* If the mtime has changed between the time we set new_modtime
|
|||
|
and now, we *want* this to be out of date, so don't call stat
|
|||
|
again now. */
|
|||
|
objfile->mtime = new_modtime;
|
|||
|
init_entry_point_info (objfile);
|
|||
|
|
|||
|
new_objfiles.push_back (objfile);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!new_objfiles.empty ())
|
|||
|
{
|
|||
|
clear_symtab_users (0);
|
|||
|
|
|||
|
/* clear_objfile_data for each objfile was called before freeing it and
|
|||
|
gdb::observers::new_objfile.notify (NULL) has been called by
|
|||
|
clear_symtab_users above. Notify the new files now. */
|
|||
|
for (auto iter : new_objfiles)
|
|||
|
gdb::observers::new_objfile.notify (iter);
|
|||
|
|
|||
|
/* At least one objfile has changed, so we can consider that
|
|||
|
the executable we're debugging has changed too. */
|
|||
|
gdb::observers::executable_changed.notify ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
struct filename_language
|
|||
|
{
|
|||
|
filename_language (const std::string &ext_, enum language lang_)
|
|||
|
: ext (ext_), lang (lang_)
|
|||
|
{}
|
|||
|
|
|||
|
std::string ext;
|
|||
|
enum language lang;
|
|||
|
};
|
|||
|
|
|||
|
static std::vector<filename_language> filename_language_table;
|
|||
|
|
|||
|
/* See symfile.h. */
|
|||
|
|
|||
|
void
|
|||
|
add_filename_language (const char *ext, enum language lang)
|
|||
|
{
|
|||
|
filename_language_table.emplace_back (ext, lang);
|
|||
|
}
|
|||
|
|
|||
|
static char *ext_args;
|
|||
|
static void
|
|||
|
show_ext_args (struct ui_file *file, int from_tty,
|
|||
|
struct cmd_list_element *c, const char *value)
|
|||
|
{
|
|||
|
fprintf_filtered (file,
|
|||
|
_("Mapping between filename extension "
|
|||
|
"and source language is \"%s\".\n"),
|
|||
|
value);
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
set_ext_lang_command (const char *args,
|
|||
|
int from_tty, struct cmd_list_element *e)
|
|||
|
{
|
|||
|
char *cp = ext_args;
|
|||
|
enum language lang;
|
|||
|
|
|||
|
/* First arg is filename extension, starting with '.' */
|
|||
|
if (*cp != '.')
|
|||
|
error (_("'%s': Filename extension must begin with '.'"), ext_args);
|
|||
|
|
|||
|
/* Find end of first arg. */
|
|||
|
while (*cp && !isspace (*cp))
|
|||
|
cp++;
|
|||
|
|
|||
|
if (*cp == '\0')
|
|||
|
error (_("'%s': two arguments required -- "
|
|||
|
"filename extension and language"),
|
|||
|
ext_args);
|
|||
|
|
|||
|
/* Null-terminate first arg. */
|
|||
|
*cp++ = '\0';
|
|||
|
|
|||
|
/* Find beginning of second arg, which should be a source language. */
|
|||
|
cp = skip_spaces (cp);
|
|||
|
|
|||
|
if (*cp == '\0')
|
|||
|
error (_("'%s': two arguments required -- "
|
|||
|
"filename extension and language"),
|
|||
|
ext_args);
|
|||
|
|
|||
|
/* Lookup the language from among those we know. */
|
|||
|
lang = language_enum (cp);
|
|||
|
|
|||
|
auto it = filename_language_table.begin ();
|
|||
|
/* Now lookup the filename extension: do we already know it? */
|
|||
|
for (; it != filename_language_table.end (); it++)
|
|||
|
{
|
|||
|
if (it->ext == ext_args)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (it == filename_language_table.end ())
|
|||
|
{
|
|||
|
/* New file extension. */
|
|||
|
add_filename_language (ext_args, lang);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Redefining a previously known filename extension. */
|
|||
|
|
|||
|
/* if (from_tty) */
|
|||
|
/* query ("Really make files of type %s '%s'?", */
|
|||
|
/* ext_args, language_str (lang)); */
|
|||
|
|
|||
|
it->lang = lang;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
info_ext_lang_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
printf_filtered (_("Filename extensions and the languages they represent:"));
|
|||
|
printf_filtered ("\n\n");
|
|||
|
for (const filename_language &entry : filename_language_table)
|
|||
|
printf_filtered ("\t%s\t- %s\n", entry.ext.c_str (),
|
|||
|
language_str (entry.lang));
|
|||
|
}
|
|||
|
|
|||
|
enum language
|
|||
|
deduce_language_from_filename (const char *filename)
|
|||
|
{
|
|||
|
const char *cp;
|
|||
|
|
|||
|
if (filename != NULL)
|
|||
|
if ((cp = strrchr (filename, '.')) != NULL)
|
|||
|
{
|
|||
|
for (const filename_language &entry : filename_language_table)
|
|||
|
if (entry.ext == cp)
|
|||
|
return entry.lang;
|
|||
|
}
|
|||
|
|
|||
|
return language_unknown;
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate and initialize a new symbol table.
|
|||
|
CUST is from the result of allocate_compunit_symtab. */
|
|||
|
|
|||
|
struct symtab *
|
|||
|
allocate_symtab (struct compunit_symtab *cust, const char *filename)
|
|||
|
{
|
|||
|
struct objfile *objfile = cust->objfile;
|
|||
|
struct symtab *symtab
|
|||
|
= OBSTACK_ZALLOC (&objfile->objfile_obstack, struct symtab);
|
|||
|
|
|||
|
symtab->filename
|
|||
|
= ((const char *) objfile->per_bfd->filename_cache.insert
|
|||
|
(filename, strlen (filename) + 1));
|
|||
|
symtab->fullname = NULL;
|
|||
|
symtab->language = deduce_language_from_filename (filename);
|
|||
|
|
|||
|
/* This can be very verbose with lots of headers.
|
|||
|
Only print at higher debug levels. */
|
|||
|
if (symtab_create_debug >= 2)
|
|||
|
{
|
|||
|
/* Be a bit clever with debugging messages, and don't print objfile
|
|||
|
every time, only when it changes. */
|
|||
|
static char *last_objfile_name = NULL;
|
|||
|
|
|||
|
if (last_objfile_name == NULL
|
|||
|
|| strcmp (last_objfile_name, objfile_name (objfile)) != 0)
|
|||
|
{
|
|||
|
xfree (last_objfile_name);
|
|||
|
last_objfile_name = xstrdup (objfile_name (objfile));
|
|||
|
fprintf_filtered (gdb_stdlog,
|
|||
|
"Creating one or more symtabs for objfile %s ...\n",
|
|||
|
last_objfile_name);
|
|||
|
}
|
|||
|
fprintf_filtered (gdb_stdlog,
|
|||
|
"Created symtab %s for module %s.\n",
|
|||
|
host_address_to_string (symtab), filename);
|
|||
|
}
|
|||
|
|
|||
|
/* Add it to CUST's list of symtabs. */
|
|||
|
if (cust->filetabs == NULL)
|
|||
|
{
|
|||
|
cust->filetabs = symtab;
|
|||
|
cust->last_filetab = symtab;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cust->last_filetab->next = symtab;
|
|||
|
cust->last_filetab = symtab;
|
|||
|
}
|
|||
|
|
|||
|
/* Backlink to the containing compunit symtab. */
|
|||
|
symtab->compunit_symtab = cust;
|
|||
|
|
|||
|
return symtab;
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate and initialize a new compunit.
|
|||
|
NAME is the name of the main source file, if there is one, or some
|
|||
|
descriptive text if there are no source files. */
|
|||
|
|
|||
|
struct compunit_symtab *
|
|||
|
allocate_compunit_symtab (struct objfile *objfile, const char *name)
|
|||
|
{
|
|||
|
struct compunit_symtab *cu = OBSTACK_ZALLOC (&objfile->objfile_obstack,
|
|||
|
struct compunit_symtab);
|
|||
|
const char *saved_name;
|
|||
|
|
|||
|
cu->objfile = objfile;
|
|||
|
|
|||
|
/* The name we record here is only for display/debugging purposes.
|
|||
|
Just save the basename to avoid path issues (too long for display,
|
|||
|
relative vs absolute, etc.). */
|
|||
|
saved_name = lbasename (name);
|
|||
|
cu->name = obstack_strdup (&objfile->objfile_obstack, saved_name);
|
|||
|
|
|||
|
COMPUNIT_DEBUGFORMAT (cu) = "unknown";
|
|||
|
|
|||
|
if (symtab_create_debug)
|
|||
|
{
|
|||
|
fprintf_filtered (gdb_stdlog,
|
|||
|
"Created compunit symtab %s for %s.\n",
|
|||
|
host_address_to_string (cu),
|
|||
|
cu->name);
|
|||
|
}
|
|||
|
|
|||
|
return cu;
|
|||
|
}
|
|||
|
|
|||
|
/* Hook CU to the objfile it comes from. */
|
|||
|
|
|||
|
void
|
|||
|
add_compunit_symtab_to_objfile (struct compunit_symtab *cu)
|
|||
|
{
|
|||
|
cu->next = cu->objfile->compunit_symtabs;
|
|||
|
cu->objfile->compunit_symtabs = cu;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Reset all data structures in gdb which may contain references to
|
|||
|
symbol table data. */
|
|||
|
|
|||
|
void
|
|||
|
clear_symtab_users (symfile_add_flags add_flags)
|
|||
|
{
|
|||
|
/* Someday, we should do better than this, by only blowing away
|
|||
|
the things that really need to be blown. */
|
|||
|
|
|||
|
/* Clear the "current" symtab first, because it is no longer valid.
|
|||
|
breakpoint_re_set may try to access the current symtab. */
|
|||
|
clear_current_source_symtab_and_line ();
|
|||
|
|
|||
|
clear_displays ();
|
|||
|
clear_last_displayed_sal ();
|
|||
|
clear_pc_function_cache ();
|
|||
|
gdb::observers::new_objfile.notify (NULL);
|
|||
|
|
|||
|
/* Varobj may refer to old symbols, perform a cleanup. */
|
|||
|
varobj_invalidate ();
|
|||
|
|
|||
|
/* Now that the various caches have been cleared, we can re_set
|
|||
|
our breakpoints without risking it using stale data. */
|
|||
|
if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
|
|||
|
breakpoint_re_set ();
|
|||
|
}
|
|||
|
|
|||
|
/* OVERLAYS:
|
|||
|
The following code implements an abstraction for debugging overlay sections.
|
|||
|
|
|||
|
The target model is as follows:
|
|||
|
1) The gnu linker will permit multiple sections to be mapped into the
|
|||
|
same VMA, each with its own unique LMA (or load address).
|
|||
|
2) It is assumed that some runtime mechanism exists for mapping the
|
|||
|
sections, one by one, from the load address into the VMA address.
|
|||
|
3) This code provides a mechanism for gdb to keep track of which
|
|||
|
sections should be considered to be mapped from the VMA to the LMA.
|
|||
|
This information is used for symbol lookup, and memory read/write.
|
|||
|
For instance, if a section has been mapped then its contents
|
|||
|
should be read from the VMA, otherwise from the LMA.
|
|||
|
|
|||
|
Two levels of debugger support for overlays are available. One is
|
|||
|
"manual", in which the debugger relies on the user to tell it which
|
|||
|
overlays are currently mapped. This level of support is
|
|||
|
implemented entirely in the core debugger, and the information about
|
|||
|
whether a section is mapped is kept in the objfile->obj_section table.
|
|||
|
|
|||
|
The second level of support is "automatic", and is only available if
|
|||
|
the target-specific code provides functionality to read the target's
|
|||
|
overlay mapping table, and translate its contents for the debugger
|
|||
|
(by updating the mapped state information in the obj_section tables).
|
|||
|
|
|||
|
The interface is as follows:
|
|||
|
User commands:
|
|||
|
overlay map <name> -- tell gdb to consider this section mapped
|
|||
|
overlay unmap <name> -- tell gdb to consider this section unmapped
|
|||
|
overlay list -- list the sections that GDB thinks are mapped
|
|||
|
overlay read-target -- get the target's state of what's mapped
|
|||
|
overlay off/manual/auto -- set overlay debugging state
|
|||
|
Functional interface:
|
|||
|
find_pc_mapped_section(pc): if the pc is in the range of a mapped
|
|||
|
section, return that section.
|
|||
|
find_pc_overlay(pc): find any overlay section that contains
|
|||
|
the pc, either in its VMA or its LMA
|
|||
|
section_is_mapped(sect): true if overlay is marked as mapped
|
|||
|
section_is_overlay(sect): true if section's VMA != LMA
|
|||
|
pc_in_mapped_range(pc,sec): true if pc belongs to section's VMA
|
|||
|
pc_in_unmapped_range(...): true if pc belongs to section's LMA
|
|||
|
sections_overlap(sec1, sec2): true if mapped sec1 and sec2 ranges overlap
|
|||
|
overlay_mapped_address(...): map an address from section's LMA to VMA
|
|||
|
overlay_unmapped_address(...): map an address from section's VMA to LMA
|
|||
|
symbol_overlayed_address(...): Return a "current" address for symbol:
|
|||
|
either in VMA or LMA depending on whether
|
|||
|
the symbol's section is currently mapped. */
|
|||
|
|
|||
|
/* Overlay debugging state: */
|
|||
|
|
|||
|
enum overlay_debugging_state overlay_debugging = ovly_off;
|
|||
|
int overlay_cache_invalid = 0; /* True if need to refresh mapped state. */
|
|||
|
|
|||
|
/* Function: section_is_overlay (SECTION)
|
|||
|
Returns true if SECTION has VMA not equal to LMA, ie.
|
|||
|
SECTION is loaded at an address different from where it will "run". */
|
|||
|
|
|||
|
int
|
|||
|
section_is_overlay (struct obj_section *section)
|
|||
|
{
|
|||
|
if (overlay_debugging && section)
|
|||
|
{
|
|||
|
asection *bfd_section = section->the_bfd_section;
|
|||
|
|
|||
|
if (bfd_section_lma (bfd_section) != 0
|
|||
|
&& bfd_section_lma (bfd_section) != bfd_section_vma (bfd_section))
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_invalidate_all (void)
|
|||
|
Invalidate the mapped state of all overlay sections (mark it as stale). */
|
|||
|
|
|||
|
static void
|
|||
|
overlay_invalidate_all (void)
|
|||
|
{
|
|||
|
struct obj_section *sect;
|
|||
|
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, sect)
|
|||
|
if (section_is_overlay (sect))
|
|||
|
sect->ovly_mapped = -1;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: section_is_mapped (SECTION)
|
|||
|
Returns true if section is an overlay, and is currently mapped.
|
|||
|
|
|||
|
Access to the ovly_mapped flag is restricted to this function, so
|
|||
|
that we can do automatic update. If the global flag
|
|||
|
OVERLAY_CACHE_INVALID is set (by wait_for_inferior), then call
|
|||
|
overlay_invalidate_all. If the mapped state of the particular
|
|||
|
section is stale, then call TARGET_OVERLAY_UPDATE to refresh it. */
|
|||
|
|
|||
|
int
|
|||
|
section_is_mapped (struct obj_section *osect)
|
|||
|
{
|
|||
|
struct gdbarch *gdbarch;
|
|||
|
|
|||
|
if (osect == 0 || !section_is_overlay (osect))
|
|||
|
return 0;
|
|||
|
|
|||
|
switch (overlay_debugging)
|
|||
|
{
|
|||
|
default:
|
|||
|
case ovly_off:
|
|||
|
return 0; /* overlay debugging off */
|
|||
|
case ovly_auto: /* overlay debugging automatic */
|
|||
|
/* Unles there is a gdbarch_overlay_update function,
|
|||
|
there's really nothing useful to do here (can't really go auto). */
|
|||
|
gdbarch = get_objfile_arch (osect->objfile);
|
|||
|
if (gdbarch_overlay_update_p (gdbarch))
|
|||
|
{
|
|||
|
if (overlay_cache_invalid)
|
|||
|
{
|
|||
|
overlay_invalidate_all ();
|
|||
|
overlay_cache_invalid = 0;
|
|||
|
}
|
|||
|
if (osect->ovly_mapped == -1)
|
|||
|
gdbarch_overlay_update (gdbarch, osect);
|
|||
|
}
|
|||
|
/* fall thru */
|
|||
|
case ovly_on: /* overlay debugging manual */
|
|||
|
return osect->ovly_mapped == 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Function: pc_in_unmapped_range
|
|||
|
If PC falls into the lma range of SECTION, return true, else false. */
|
|||
|
|
|||
|
CORE_ADDR
|
|||
|
pc_in_unmapped_range (CORE_ADDR pc, struct obj_section *section)
|
|||
|
{
|
|||
|
if (section_is_overlay (section))
|
|||
|
{
|
|||
|
asection *bfd_section = section->the_bfd_section;
|
|||
|
|
|||
|
/* We assume the LMA is relocated by the same offset as the VMA. */
|
|||
|
bfd_vma size = bfd_section_size (bfd_section);
|
|||
|
CORE_ADDR offset = obj_section_offset (section);
|
|||
|
|
|||
|
if (bfd_section_lma (bfd_section) + offset <= pc
|
|||
|
&& pc < bfd_section_lma (bfd_section) + offset + size)
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: pc_in_mapped_range
|
|||
|
If PC falls into the vma range of SECTION, return true, else false. */
|
|||
|
|
|||
|
CORE_ADDR
|
|||
|
pc_in_mapped_range (CORE_ADDR pc, struct obj_section *section)
|
|||
|
{
|
|||
|
if (section_is_overlay (section))
|
|||
|
{
|
|||
|
if (obj_section_addr (section) <= pc
|
|||
|
&& pc < obj_section_endaddr (section))
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Return true if the mapped ranges of sections A and B overlap, false
|
|||
|
otherwise. */
|
|||
|
|
|||
|
static int
|
|||
|
sections_overlap (struct obj_section *a, struct obj_section *b)
|
|||
|
{
|
|||
|
CORE_ADDR a_start = obj_section_addr (a);
|
|||
|
CORE_ADDR a_end = obj_section_endaddr (a);
|
|||
|
CORE_ADDR b_start = obj_section_addr (b);
|
|||
|
CORE_ADDR b_end = obj_section_endaddr (b);
|
|||
|
|
|||
|
return (a_start < b_end && b_start < a_end);
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_unmapped_address (PC, SECTION)
|
|||
|
Returns the address corresponding to PC in the unmapped (load) range.
|
|||
|
May be the same as PC. */
|
|||
|
|
|||
|
CORE_ADDR
|
|||
|
overlay_unmapped_address (CORE_ADDR pc, struct obj_section *section)
|
|||
|
{
|
|||
|
if (section_is_overlay (section) && pc_in_mapped_range (pc, section))
|
|||
|
{
|
|||
|
asection *bfd_section = section->the_bfd_section;
|
|||
|
|
|||
|
return (pc + bfd_section_lma (bfd_section)
|
|||
|
- bfd_section_vma (bfd_section));
|
|||
|
}
|
|||
|
|
|||
|
return pc;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_mapped_address (PC, SECTION)
|
|||
|
Returns the address corresponding to PC in the mapped (runtime) range.
|
|||
|
May be the same as PC. */
|
|||
|
|
|||
|
CORE_ADDR
|
|||
|
overlay_mapped_address (CORE_ADDR pc, struct obj_section *section)
|
|||
|
{
|
|||
|
if (section_is_overlay (section) && pc_in_unmapped_range (pc, section))
|
|||
|
{
|
|||
|
asection *bfd_section = section->the_bfd_section;
|
|||
|
|
|||
|
return (pc + bfd_section_vma (bfd_section)
|
|||
|
- bfd_section_lma (bfd_section));
|
|||
|
}
|
|||
|
|
|||
|
return pc;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: symbol_overlayed_address
|
|||
|
Return one of two addresses (relative to the VMA or to the LMA),
|
|||
|
depending on whether the section is mapped or not. */
|
|||
|
|
|||
|
CORE_ADDR
|
|||
|
symbol_overlayed_address (CORE_ADDR address, struct obj_section *section)
|
|||
|
{
|
|||
|
if (overlay_debugging)
|
|||
|
{
|
|||
|
/* If the symbol has no section, just return its regular address. */
|
|||
|
if (section == 0)
|
|||
|
return address;
|
|||
|
/* If the symbol's section is not an overlay, just return its
|
|||
|
address. */
|
|||
|
if (!section_is_overlay (section))
|
|||
|
return address;
|
|||
|
/* If the symbol's section is mapped, just return its address. */
|
|||
|
if (section_is_mapped (section))
|
|||
|
return address;
|
|||
|
/*
|
|||
|
* HOWEVER: if the symbol is in an overlay section which is NOT mapped,
|
|||
|
* then return its LOADED address rather than its vma address!!
|
|||
|
*/
|
|||
|
return overlay_unmapped_address (address, section);
|
|||
|
}
|
|||
|
return address;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: find_pc_overlay (PC)
|
|||
|
Return the best-match overlay section for PC:
|
|||
|
If PC matches a mapped overlay section's VMA, return that section.
|
|||
|
Else if PC matches an unmapped section's VMA, return that section.
|
|||
|
Else if PC matches an unmapped section's LMA, return that section. */
|
|||
|
|
|||
|
struct obj_section *
|
|||
|
find_pc_overlay (CORE_ADDR pc)
|
|||
|
{
|
|||
|
struct obj_section *osect, *best_match = NULL;
|
|||
|
|
|||
|
if (overlay_debugging)
|
|||
|
{
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, osect)
|
|||
|
if (section_is_overlay (osect))
|
|||
|
{
|
|||
|
if (pc_in_mapped_range (pc, osect))
|
|||
|
{
|
|||
|
if (section_is_mapped (osect))
|
|||
|
return osect;
|
|||
|
else
|
|||
|
best_match = osect;
|
|||
|
}
|
|||
|
else if (pc_in_unmapped_range (pc, osect))
|
|||
|
best_match = osect;
|
|||
|
}
|
|||
|
}
|
|||
|
return best_match;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: find_pc_mapped_section (PC)
|
|||
|
If PC falls into the VMA address range of an overlay section that is
|
|||
|
currently marked as MAPPED, return that section. Else return NULL. */
|
|||
|
|
|||
|
struct obj_section *
|
|||
|
find_pc_mapped_section (CORE_ADDR pc)
|
|||
|
{
|
|||
|
struct obj_section *osect;
|
|||
|
|
|||
|
if (overlay_debugging)
|
|||
|
{
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, osect)
|
|||
|
if (pc_in_mapped_range (pc, osect) && section_is_mapped (osect))
|
|||
|
return osect;
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: list_overlays_command
|
|||
|
Print a list of mapped sections and their PC ranges. */
|
|||
|
|
|||
|
static void
|
|||
|
list_overlays_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
int nmapped = 0;
|
|||
|
struct obj_section *osect;
|
|||
|
|
|||
|
if (overlay_debugging)
|
|||
|
{
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, osect)
|
|||
|
if (section_is_mapped (osect))
|
|||
|
{
|
|||
|
struct gdbarch *gdbarch = get_objfile_arch (objfile);
|
|||
|
const char *name;
|
|||
|
bfd_vma lma, vma;
|
|||
|
int size;
|
|||
|
|
|||
|
vma = bfd_section_vma (osect->the_bfd_section);
|
|||
|
lma = bfd_section_lma (osect->the_bfd_section);
|
|||
|
size = bfd_section_size (osect->the_bfd_section);
|
|||
|
name = bfd_section_name (osect->the_bfd_section);
|
|||
|
|
|||
|
printf_filtered ("Section %s, loaded at ", name);
|
|||
|
fputs_filtered (paddress (gdbarch, lma), gdb_stdout);
|
|||
|
puts_filtered (" - ");
|
|||
|
fputs_filtered (paddress (gdbarch, lma + size), gdb_stdout);
|
|||
|
printf_filtered (", mapped at ");
|
|||
|
fputs_filtered (paddress (gdbarch, vma), gdb_stdout);
|
|||
|
puts_filtered (" - ");
|
|||
|
fputs_filtered (paddress (gdbarch, vma + size), gdb_stdout);
|
|||
|
puts_filtered ("\n");
|
|||
|
|
|||
|
nmapped++;
|
|||
|
}
|
|||
|
}
|
|||
|
if (nmapped == 0)
|
|||
|
printf_filtered (_("No sections are mapped.\n"));
|
|||
|
}
|
|||
|
|
|||
|
/* Function: map_overlay_command
|
|||
|
Mark the named section as mapped (ie. residing at its VMA address). */
|
|||
|
|
|||
|
static void
|
|||
|
map_overlay_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct obj_section *sec, *sec2;
|
|||
|
|
|||
|
if (!overlay_debugging)
|
|||
|
error (_("Overlay debugging not enabled. Use "
|
|||
|
"either the 'overlay auto' or\n"
|
|||
|
"the 'overlay manual' command."));
|
|||
|
|
|||
|
if (args == 0 || *args == 0)
|
|||
|
error (_("Argument required: name of an overlay section"));
|
|||
|
|
|||
|
/* First, find a section matching the user supplied argument. */
|
|||
|
for (objfile *obj_file : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (obj_file, sec)
|
|||
|
if (!strcmp (bfd_section_name (sec->the_bfd_section), args))
|
|||
|
{
|
|||
|
/* Now, check to see if the section is an overlay. */
|
|||
|
if (!section_is_overlay (sec))
|
|||
|
continue; /* not an overlay section */
|
|||
|
|
|||
|
/* Mark the overlay as "mapped". */
|
|||
|
sec->ovly_mapped = 1;
|
|||
|
|
|||
|
/* Next, make a pass and unmap any sections that are
|
|||
|
overlapped by this new section: */
|
|||
|
for (objfile *objfile2 : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile2, sec2)
|
|||
|
if (sec2->ovly_mapped && sec != sec2 && sections_overlap (sec,
|
|||
|
sec2))
|
|||
|
{
|
|||
|
if (info_verbose)
|
|||
|
printf_unfiltered (_("Note: section %s unmapped by overlap\n"),
|
|||
|
bfd_section_name (sec2->the_bfd_section));
|
|||
|
sec2->ovly_mapped = 0; /* sec2 overlaps sec: unmap sec2. */
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
error (_("No overlay section called %s"), args);
|
|||
|
}
|
|||
|
|
|||
|
/* Function: unmap_overlay_command
|
|||
|
Mark the overlay section as unmapped
|
|||
|
(ie. resident in its LMA address range, rather than the VMA range). */
|
|||
|
|
|||
|
static void
|
|||
|
unmap_overlay_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct obj_section *sec = NULL;
|
|||
|
|
|||
|
if (!overlay_debugging)
|
|||
|
error (_("Overlay debugging not enabled. "
|
|||
|
"Use either the 'overlay auto' or\n"
|
|||
|
"the 'overlay manual' command."));
|
|||
|
|
|||
|
if (args == 0 || *args == 0)
|
|||
|
error (_("Argument required: name of an overlay section"));
|
|||
|
|
|||
|
/* First, find a section matching the user supplied argument. */
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, sec)
|
|||
|
if (!strcmp (bfd_section_name (sec->the_bfd_section), args))
|
|||
|
{
|
|||
|
if (!sec->ovly_mapped)
|
|||
|
error (_("Section %s is not mapped"), args);
|
|||
|
sec->ovly_mapped = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
error (_("No overlay section called %s"), args);
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_auto_command
|
|||
|
A utility command to turn on overlay debugging.
|
|||
|
Possibly this should be done via a set/show command. */
|
|||
|
|
|||
|
static void
|
|||
|
overlay_auto_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
overlay_debugging = ovly_auto;
|
|||
|
enable_overlay_breakpoints ();
|
|||
|
if (info_verbose)
|
|||
|
printf_unfiltered (_("Automatic overlay debugging enabled."));
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_manual_command
|
|||
|
A utility command to turn on overlay debugging.
|
|||
|
Possibly this should be done via a set/show command. */
|
|||
|
|
|||
|
static void
|
|||
|
overlay_manual_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
overlay_debugging = ovly_on;
|
|||
|
disable_overlay_breakpoints ();
|
|||
|
if (info_verbose)
|
|||
|
printf_unfiltered (_("Overlay debugging enabled."));
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_off_command
|
|||
|
A utility command to turn on overlay debugging.
|
|||
|
Possibly this should be done via a set/show command. */
|
|||
|
|
|||
|
static void
|
|||
|
overlay_off_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
overlay_debugging = ovly_off;
|
|||
|
disable_overlay_breakpoints ();
|
|||
|
if (info_verbose)
|
|||
|
printf_unfiltered (_("Overlay debugging disabled."));
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
overlay_load_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
struct gdbarch *gdbarch = get_current_arch ();
|
|||
|
|
|||
|
if (gdbarch_overlay_update_p (gdbarch))
|
|||
|
gdbarch_overlay_update (gdbarch, NULL);
|
|||
|
else
|
|||
|
error (_("This target does not know how to read its overlay state."));
|
|||
|
}
|
|||
|
|
|||
|
/* Function: overlay_command
|
|||
|
A place-holder for a mis-typed command. */
|
|||
|
|
|||
|
/* Command list chain containing all defined "overlay" subcommands. */
|
|||
|
static struct cmd_list_element *overlaylist;
|
|||
|
|
|||
|
static void
|
|||
|
overlay_command (const char *args, int from_tty)
|
|||
|
{
|
|||
|
printf_unfiltered
|
|||
|
("\"overlay\" must be followed by the name of an overlay command.\n");
|
|||
|
help_list (overlaylist, "overlay ", all_commands, gdb_stdout);
|
|||
|
}
|
|||
|
|
|||
|
/* Target Overlays for the "Simplest" overlay manager:
|
|||
|
|
|||
|
This is GDB's default target overlay layer. It works with the
|
|||
|
minimal overlay manager supplied as an example by Cygnus. The
|
|||
|
entry point is via a function pointer "gdbarch_overlay_update",
|
|||
|
so targets that use a different runtime overlay manager can
|
|||
|
substitute their own overlay_update function and take over the
|
|||
|
function pointer.
|
|||
|
|
|||
|
The overlay_update function pokes around in the target's data structures
|
|||
|
to see what overlays are mapped, and updates GDB's overlay mapping with
|
|||
|
this information.
|
|||
|
|
|||
|
In this simple implementation, the target data structures are as follows:
|
|||
|
unsigned _novlys; /# number of overlay sections #/
|
|||
|
unsigned _ovly_table[_novlys][4] = {
|
|||
|
{VMA, OSIZE, LMA, MAPPED}, /# one entry per overlay section #/
|
|||
|
{..., ..., ..., ...},
|
|||
|
}
|
|||
|
unsigned _novly_regions; /# number of overlay regions #/
|
|||
|
unsigned _ovly_region_table[_novly_regions][3] = {
|
|||
|
{VMA, OSIZE, MAPPED_TO_LMA}, /# one entry per overlay region #/
|
|||
|
{..., ..., ...},
|
|||
|
}
|
|||
|
These functions will attempt to update GDB's mappedness state in the
|
|||
|
symbol section table, based on the target's mappedness state.
|
|||
|
|
|||
|
To do this, we keep a cached copy of the target's _ovly_table, and
|
|||
|
attempt to detect when the cached copy is invalidated. The main
|
|||
|
entry point is "simple_overlay_update(SECT), which looks up SECT in
|
|||
|
the cached table and re-reads only the entry for that section from
|
|||
|
the target (whenever possible). */
|
|||
|
|
|||
|
/* Cached, dynamically allocated copies of the target data structures: */
|
|||
|
static unsigned (*cache_ovly_table)[4] = 0;
|
|||
|
static unsigned cache_novlys = 0;
|
|||
|
static CORE_ADDR cache_ovly_table_base = 0;
|
|||
|
enum ovly_index
|
|||
|
{
|
|||
|
VMA, OSIZE, LMA, MAPPED
|
|||
|
};
|
|||
|
|
|||
|
/* Throw away the cached copy of _ovly_table. */
|
|||
|
|
|||
|
static void
|
|||
|
simple_free_overlay_table (void)
|
|||
|
{
|
|||
|
if (cache_ovly_table)
|
|||
|
xfree (cache_ovly_table);
|
|||
|
cache_novlys = 0;
|
|||
|
cache_ovly_table = NULL;
|
|||
|
cache_ovly_table_base = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Read an array of ints of size SIZE from the target into a local buffer.
|
|||
|
Convert to host order. int LEN is number of ints. */
|
|||
|
|
|||
|
static void
|
|||
|
read_target_long_array (CORE_ADDR memaddr, unsigned int *myaddr,
|
|||
|
int len, int size, enum bfd_endian byte_order)
|
|||
|
{
|
|||
|
/* FIXME (alloca): Not safe if array is very large. */
|
|||
|
gdb_byte *buf = (gdb_byte *) alloca (len * size);
|
|||
|
int i;
|
|||
|
|
|||
|
read_memory (memaddr, buf, len * size);
|
|||
|
for (i = 0; i < len; i++)
|
|||
|
myaddr[i] = extract_unsigned_integer (size * i + buf, size, byte_order);
|
|||
|
}
|
|||
|
|
|||
|
/* Find and grab a copy of the target _ovly_table
|
|||
|
(and _novlys, which is needed for the table's size). */
|
|||
|
|
|||
|
static int
|
|||
|
simple_read_overlay_table (void)
|
|||
|
{
|
|||
|
struct bound_minimal_symbol novlys_msym;
|
|||
|
struct bound_minimal_symbol ovly_table_msym;
|
|||
|
struct gdbarch *gdbarch;
|
|||
|
int word_size;
|
|||
|
enum bfd_endian byte_order;
|
|||
|
|
|||
|
simple_free_overlay_table ();
|
|||
|
novlys_msym = lookup_minimal_symbol ("_novlys", NULL, NULL);
|
|||
|
if (! novlys_msym.minsym)
|
|||
|
{
|
|||
|
error (_("Error reading inferior's overlay table: "
|
|||
|
"couldn't find `_novlys' variable\n"
|
|||
|
"in inferior. Use `overlay manual' mode."));
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
ovly_table_msym = lookup_bound_minimal_symbol ("_ovly_table");
|
|||
|
if (! ovly_table_msym.minsym)
|
|||
|
{
|
|||
|
error (_("Error reading inferior's overlay table: couldn't find "
|
|||
|
"`_ovly_table' array\n"
|
|||
|
"in inferior. Use `overlay manual' mode."));
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
gdbarch = get_objfile_arch (ovly_table_msym.objfile);
|
|||
|
word_size = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
|
|||
|
byte_order = gdbarch_byte_order (gdbarch);
|
|||
|
|
|||
|
cache_novlys = read_memory_integer (BMSYMBOL_VALUE_ADDRESS (novlys_msym),
|
|||
|
4, byte_order);
|
|||
|
cache_ovly_table
|
|||
|
= (unsigned int (*)[4]) xmalloc (cache_novlys * sizeof (*cache_ovly_table));
|
|||
|
cache_ovly_table_base = BMSYMBOL_VALUE_ADDRESS (ovly_table_msym);
|
|||
|
read_target_long_array (cache_ovly_table_base,
|
|||
|
(unsigned int *) cache_ovly_table,
|
|||
|
cache_novlys * 4, word_size, byte_order);
|
|||
|
|
|||
|
return 1; /* SUCCESS */
|
|||
|
}
|
|||
|
|
|||
|
/* Function: simple_overlay_update_1
|
|||
|
A helper function for simple_overlay_update. Assuming a cached copy
|
|||
|
of _ovly_table exists, look through it to find an entry whose vma,
|
|||
|
lma and size match those of OSECT. Re-read the entry and make sure
|
|||
|
it still matches OSECT (else the table may no longer be valid).
|
|||
|
Set OSECT's mapped state to match the entry. Return: 1 for
|
|||
|
success, 0 for failure. */
|
|||
|
|
|||
|
static int
|
|||
|
simple_overlay_update_1 (struct obj_section *osect)
|
|||
|
{
|
|||
|
int i;
|
|||
|
asection *bsect = osect->the_bfd_section;
|
|||
|
struct gdbarch *gdbarch = get_objfile_arch (osect->objfile);
|
|||
|
int word_size = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
|
|||
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|||
|
|
|||
|
for (i = 0; i < cache_novlys; i++)
|
|||
|
if (cache_ovly_table[i][VMA] == bfd_section_vma (bsect)
|
|||
|
&& cache_ovly_table[i][LMA] == bfd_section_lma (bsect))
|
|||
|
{
|
|||
|
read_target_long_array (cache_ovly_table_base + i * word_size,
|
|||
|
(unsigned int *) cache_ovly_table[i],
|
|||
|
4, word_size, byte_order);
|
|||
|
if (cache_ovly_table[i][VMA] == bfd_section_vma (bsect)
|
|||
|
&& cache_ovly_table[i][LMA] == bfd_section_lma (bsect))
|
|||
|
{
|
|||
|
osect->ovly_mapped = cache_ovly_table[i][MAPPED];
|
|||
|
return 1;
|
|||
|
}
|
|||
|
else /* Warning! Warning! Target's ovly table has changed! */
|
|||
|
return 0;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Function: simple_overlay_update
|
|||
|
If OSECT is NULL, then update all sections' mapped state
|
|||
|
(after re-reading the entire target _ovly_table).
|
|||
|
If OSECT is non-NULL, then try to find a matching entry in the
|
|||
|
cached ovly_table and update only OSECT's mapped state.
|
|||
|
If a cached entry can't be found or the cache isn't valid, then
|
|||
|
re-read the entire cache, and go ahead and update all sections. */
|
|||
|
|
|||
|
void
|
|||
|
simple_overlay_update (struct obj_section *osect)
|
|||
|
{
|
|||
|
/* Were we given an osect to look up? NULL means do all of them. */
|
|||
|
if (osect)
|
|||
|
/* Have we got a cached copy of the target's overlay table? */
|
|||
|
if (cache_ovly_table != NULL)
|
|||
|
{
|
|||
|
/* Does its cached location match what's currently in the
|
|||
|
symtab? */
|
|||
|
struct bound_minimal_symbol minsym
|
|||
|
= lookup_minimal_symbol ("_ovly_table", NULL, NULL);
|
|||
|
|
|||
|
if (minsym.minsym == NULL)
|
|||
|
error (_("Error reading inferior's overlay table: couldn't "
|
|||
|
"find `_ovly_table' array\n"
|
|||
|
"in inferior. Use `overlay manual' mode."));
|
|||
|
|
|||
|
if (cache_ovly_table_base == BMSYMBOL_VALUE_ADDRESS (minsym))
|
|||
|
/* Then go ahead and try to look up this single section in
|
|||
|
the cache. */
|
|||
|
if (simple_overlay_update_1 (osect))
|
|||
|
/* Found it! We're done. */
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Cached table no good: need to read the entire table anew.
|
|||
|
Or else we want all the sections, in which case it's actually
|
|||
|
more efficient to read the whole table in one block anyway. */
|
|||
|
|
|||
|
if (! simple_read_overlay_table ())
|
|||
|
return;
|
|||
|
|
|||
|
/* Now may as well update all sections, even if only one was requested. */
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
ALL_OBJFILE_OSECTIONS (objfile, osect)
|
|||
|
if (section_is_overlay (osect))
|
|||
|
{
|
|||
|
int i;
|
|||
|
asection *bsect = osect->the_bfd_section;
|
|||
|
|
|||
|
for (i = 0; i < cache_novlys; i++)
|
|||
|
if (cache_ovly_table[i][VMA] == bfd_section_vma (bsect)
|
|||
|
&& cache_ovly_table[i][LMA] == bfd_section_lma (bsect))
|
|||
|
{ /* obj_section matches i'th entry in ovly_table. */
|
|||
|
osect->ovly_mapped = cache_ovly_table[i][MAPPED];
|
|||
|
break; /* finished with inner for loop: break out. */
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Set the output sections and output offsets for section SECTP in
|
|||
|
ABFD. The relocation code in BFD will read these offsets, so we
|
|||
|
need to be sure they're initialized. We map each section to itself,
|
|||
|
with no offset; this means that SECTP->vma will be honored. */
|
|||
|
|
|||
|
static void
|
|||
|
symfile_dummy_outputs (bfd *abfd, asection *sectp, void *dummy)
|
|||
|
{
|
|||
|
sectp->output_section = sectp;
|
|||
|
sectp->output_offset = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Default implementation for sym_relocate. */
|
|||
|
|
|||
|
bfd_byte *
|
|||
|
default_symfile_relocate (struct objfile *objfile, asection *sectp,
|
|||
|
bfd_byte *buf)
|
|||
|
{
|
|||
|
/* Use sectp->owner instead of objfile->obfd. sectp may point to a
|
|||
|
DWO file. */
|
|||
|
bfd *abfd = sectp->owner;
|
|||
|
|
|||
|
/* We're only interested in sections with relocation
|
|||
|
information. */
|
|||
|
if ((sectp->flags & SEC_RELOC) == 0)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* We will handle section offsets properly elsewhere, so relocate as if
|
|||
|
all sections begin at 0. */
|
|||
|
bfd_map_over_sections (abfd, symfile_dummy_outputs, NULL);
|
|||
|
|
|||
|
return bfd_simple_get_relocated_section_contents (abfd, sectp, buf, NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* Relocate the contents of a debug section SECTP in ABFD. The
|
|||
|
contents are stored in BUF if it is non-NULL, or returned in a
|
|||
|
malloc'd buffer otherwise.
|
|||
|
|
|||
|
For some platforms and debug info formats, shared libraries contain
|
|||
|
relocations against the debug sections (particularly for DWARF-2;
|
|||
|
one affected platform is PowerPC GNU/Linux, although it depends on
|
|||
|
the version of the linker in use). Also, ELF object files naturally
|
|||
|
have unresolved relocations for their debug sections. We need to apply
|
|||
|
the relocations in order to get the locations of symbols correct.
|
|||
|
Another example that may require relocation processing, is the
|
|||
|
DWARF-2 .eh_frame section in .o files, although it isn't strictly a
|
|||
|
debug section. */
|
|||
|
|
|||
|
bfd_byte *
|
|||
|
symfile_relocate_debug_section (struct objfile *objfile,
|
|||
|
asection *sectp, bfd_byte *buf)
|
|||
|
{
|
|||
|
gdb_assert (objfile->sf->sym_relocate);
|
|||
|
|
|||
|
return (*objfile->sf->sym_relocate) (objfile, sectp, buf);
|
|||
|
}
|
|||
|
|
|||
|
struct symfile_segment_data *
|
|||
|
get_symfile_segment_data (bfd *abfd)
|
|||
|
{
|
|||
|
const struct sym_fns *sf = find_sym_fns (abfd);
|
|||
|
|
|||
|
if (sf == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
return sf->sym_segments (abfd);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
free_symfile_segment_data (struct symfile_segment_data *data)
|
|||
|
{
|
|||
|
xfree (data->segment_bases);
|
|||
|
xfree (data->segment_sizes);
|
|||
|
xfree (data->segment_info);
|
|||
|
xfree (data);
|
|||
|
}
|
|||
|
|
|||
|
/* Given:
|
|||
|
- DATA, containing segment addresses from the object file ABFD, and
|
|||
|
the mapping from ABFD's sections onto the segments that own them,
|
|||
|
and
|
|||
|
- SEGMENT_BASES[0 .. NUM_SEGMENT_BASES - 1], holding the actual
|
|||
|
segment addresses reported by the target,
|
|||
|
store the appropriate offsets for each section in OFFSETS.
|
|||
|
|
|||
|
If there are fewer entries in SEGMENT_BASES than there are segments
|
|||
|
in DATA, then apply SEGMENT_BASES' last entry to all the segments.
|
|||
|
|
|||
|
If there are more entries, then ignore the extra. The target may
|
|||
|
not be able to distinguish between an empty data segment and a
|
|||
|
missing data segment; a missing text segment is less plausible. */
|
|||
|
|
|||
|
int
|
|||
|
symfile_map_offsets_to_segments (bfd *abfd,
|
|||
|
const struct symfile_segment_data *data,
|
|||
|
struct section_offsets *offsets,
|
|||
|
int num_segment_bases,
|
|||
|
const CORE_ADDR *segment_bases)
|
|||
|
{
|
|||
|
int i;
|
|||
|
asection *sect;
|
|||
|
|
|||
|
/* It doesn't make sense to call this function unless you have some
|
|||
|
segment base addresses. */
|
|||
|
gdb_assert (num_segment_bases > 0);
|
|||
|
|
|||
|
/* If we do not have segment mappings for the object file, we
|
|||
|
can not relocate it by segments. */
|
|||
|
gdb_assert (data != NULL);
|
|||
|
gdb_assert (data->num_segments > 0);
|
|||
|
|
|||
|
for (i = 0, sect = abfd->sections; sect != NULL; i++, sect = sect->next)
|
|||
|
{
|
|||
|
int which = data->segment_info[i];
|
|||
|
|
|||
|
gdb_assert (0 <= which && which <= data->num_segments);
|
|||
|
|
|||
|
/* Don't bother computing offsets for sections that aren't
|
|||
|
loaded as part of any segment. */
|
|||
|
if (! which)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Use the last SEGMENT_BASES entry as the address of any extra
|
|||
|
segments mentioned in DATA->segment_info. */
|
|||
|
if (which > num_segment_bases)
|
|||
|
which = num_segment_bases;
|
|||
|
|
|||
|
offsets->offsets[i] = (segment_bases[which - 1]
|
|||
|
- data->segment_bases[which - 1]);
|
|||
|
}
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
symfile_find_segment_sections (struct objfile *objfile)
|
|||
|
{
|
|||
|
bfd *abfd = objfile->obfd;
|
|||
|
int i;
|
|||
|
asection *sect;
|
|||
|
struct symfile_segment_data *data;
|
|||
|
|
|||
|
data = get_symfile_segment_data (objfile->obfd);
|
|||
|
if (data == NULL)
|
|||
|
return;
|
|||
|
|
|||
|
if (data->num_segments != 1 && data->num_segments != 2)
|
|||
|
{
|
|||
|
free_symfile_segment_data (data);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0, sect = abfd->sections; sect != NULL; i++, sect = sect->next)
|
|||
|
{
|
|||
|
int which = data->segment_info[i];
|
|||
|
|
|||
|
if (which == 1)
|
|||
|
{
|
|||
|
if (objfile->sect_index_text == -1)
|
|||
|
objfile->sect_index_text = sect->index;
|
|||
|
|
|||
|
if (objfile->sect_index_rodata == -1)
|
|||
|
objfile->sect_index_rodata = sect->index;
|
|||
|
}
|
|||
|
else if (which == 2)
|
|||
|
{
|
|||
|
if (objfile->sect_index_data == -1)
|
|||
|
objfile->sect_index_data = sect->index;
|
|||
|
|
|||
|
if (objfile->sect_index_bss == -1)
|
|||
|
objfile->sect_index_bss = sect->index;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
free_symfile_segment_data (data);
|
|||
|
}
|
|||
|
|
|||
|
/* Listen for free_objfile events. */
|
|||
|
|
|||
|
static void
|
|||
|
symfile_free_objfile (struct objfile *objfile)
|
|||
|
{
|
|||
|
/* Remove the target sections owned by this objfile. */
|
|||
|
if (objfile != NULL)
|
|||
|
remove_target_sections ((void *) objfile);
|
|||
|
}
|
|||
|
|
|||
|
/* Wrapper around the quick_symbol_functions expand_symtabs_matching "method".
|
|||
|
Expand all symtabs that match the specified criteria.
|
|||
|
See quick_symbol_functions.expand_symtabs_matching for details. */
|
|||
|
|
|||
|
void
|
|||
|
expand_symtabs_matching
|
|||
|
(gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
|
|||
|
const lookup_name_info &lookup_name,
|
|||
|
gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
|
|||
|
gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
|
|||
|
enum search_domain kind)
|
|||
|
{
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
{
|
|||
|
if (objfile->sf)
|
|||
|
objfile->sf->qf->expand_symtabs_matching (objfile, file_matcher,
|
|||
|
lookup_name,
|
|||
|
symbol_matcher,
|
|||
|
expansion_notify, kind);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Wrapper around the quick_symbol_functions map_symbol_filenames "method".
|
|||
|
Map function FUN over every file.
|
|||
|
See quick_symbol_functions.map_symbol_filenames for details. */
|
|||
|
|
|||
|
void
|
|||
|
map_symbol_filenames (symbol_filename_ftype *fun, void *data,
|
|||
|
int need_fullname)
|
|||
|
{
|
|||
|
for (objfile *objfile : current_program_space->objfiles ())
|
|||
|
{
|
|||
|
if (objfile->sf)
|
|||
|
objfile->sf->qf->map_symbol_filenames (objfile, fun, data,
|
|||
|
need_fullname);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if GDB_SELF_TEST
|
|||
|
|
|||
|
namespace selftests {
|
|||
|
namespace filename_language {
|
|||
|
|
|||
|
static void test_filename_language ()
|
|||
|
{
|
|||
|
/* This test messes up the filename_language_table global. */
|
|||
|
scoped_restore restore_flt = make_scoped_restore (&filename_language_table);
|
|||
|
|
|||
|
/* Test deducing an unknown extension. */
|
|||
|
language lang = deduce_language_from_filename ("myfile.blah");
|
|||
|
SELF_CHECK (lang == language_unknown);
|
|||
|
|
|||
|
/* Test deducing a known extension. */
|
|||
|
lang = deduce_language_from_filename ("myfile.c");
|
|||
|
SELF_CHECK (lang == language_c);
|
|||
|
|
|||
|
/* Test adding a new extension using the internal API. */
|
|||
|
add_filename_language (".blah", language_pascal);
|
|||
|
lang = deduce_language_from_filename ("myfile.blah");
|
|||
|
SELF_CHECK (lang == language_pascal);
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
test_set_ext_lang_command ()
|
|||
|
{
|
|||
|
/* This test messes up the filename_language_table global. */
|
|||
|
scoped_restore restore_flt = make_scoped_restore (&filename_language_table);
|
|||
|
|
|||
|
/* Confirm that the .hello extension is not known. */
|
|||
|
language lang = deduce_language_from_filename ("cake.hello");
|
|||
|
SELF_CHECK (lang == language_unknown);
|
|||
|
|
|||
|
/* Test adding a new extension using the CLI command. */
|
|||
|
auto args_holder = make_unique_xstrdup (".hello rust");
|
|||
|
ext_args = args_holder.get ();
|
|||
|
set_ext_lang_command (NULL, 1, NULL);
|
|||
|
|
|||
|
lang = deduce_language_from_filename ("cake.hello");
|
|||
|
SELF_CHECK (lang == language_rust);
|
|||
|
|
|||
|
/* Test overriding an existing extension using the CLI command. */
|
|||
|
int size_before = filename_language_table.size ();
|
|||
|
args_holder.reset (xstrdup (".hello pascal"));
|
|||
|
ext_args = args_holder.get ();
|
|||
|
set_ext_lang_command (NULL, 1, NULL);
|
|||
|
int size_after = filename_language_table.size ();
|
|||
|
|
|||
|
lang = deduce_language_from_filename ("cake.hello");
|
|||
|
SELF_CHECK (lang == language_pascal);
|
|||
|
SELF_CHECK (size_before == size_after);
|
|||
|
}
|
|||
|
|
|||
|
} /* namespace filename_language */
|
|||
|
} /* namespace selftests */
|
|||
|
|
|||
|
#endif /* GDB_SELF_TEST */
|
|||
|
|
|||
|
void
|
|||
|
_initialize_symfile (void)
|
|||
|
{
|
|||
|
struct cmd_list_element *c;
|
|||
|
|
|||
|
gdb::observers::free_objfile.attach (symfile_free_objfile);
|
|||
|
|
|||
|
#define READNOW_READNEVER_HELP \
|
|||
|
"The '-readnow' option will cause GDB to read the entire symbol file\n\
|
|||
|
immediately. This makes the command slower, but may make future operations\n\
|
|||
|
faster.\n\
|
|||
|
The '-readnever' option will prevent GDB from reading the symbol file's\n\
|
|||
|
symbolic debug information."
|
|||
|
|
|||
|
c = add_cmd ("symbol-file", class_files, symbol_file_command, _("\
|
|||
|
Load symbol table from executable file FILE.\n\
|
|||
|
Usage: symbol-file [-readnow | -readnever] [-o OFF] FILE\n\
|
|||
|
OFF is an optional offset which is added to each section address.\n\
|
|||
|
The `file' command can also load symbol tables, as well as setting the file\n\
|
|||
|
to execute.\n" READNOW_READNEVER_HELP), &cmdlist);
|
|||
|
set_cmd_completer (c, filename_completer);
|
|||
|
|
|||
|
c = add_cmd ("add-symbol-file", class_files, add_symbol_file_command, _("\
|
|||
|
Load symbols from FILE, assuming FILE has been dynamically loaded.\n\
|
|||
|
Usage: add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] \
|
|||
|
[-s SECT-NAME SECT-ADDR]...\n\
|
|||
|
ADDR is the starting address of the file's text.\n\
|
|||
|
Each '-s' argument provides a section name and address, and\n\
|
|||
|
should be specified if the data and bss segments are not contiguous\n\
|
|||
|
with the text. SECT-NAME is a section name to be loaded at SECT-ADDR.\n\
|
|||
|
OFF is an optional offset which is added to the default load addresses\n\
|
|||
|
of all sections for which no other address was specified.\n"
|
|||
|
READNOW_READNEVER_HELP),
|
|||
|
&cmdlist);
|
|||
|
set_cmd_completer (c, filename_completer);
|
|||
|
|
|||
|
c = add_cmd ("remove-symbol-file", class_files,
|
|||
|
remove_symbol_file_command, _("\
|
|||
|
Remove a symbol file added via the add-symbol-file command.\n\
|
|||
|
Usage: remove-symbol-file FILENAME\n\
|
|||
|
remove-symbol-file -a ADDRESS\n\
|
|||
|
The file to remove can be identified by its filename or by an address\n\
|
|||
|
that lies within the boundaries of this symbol file in memory."),
|
|||
|
&cmdlist);
|
|||
|
|
|||
|
c = add_cmd ("load", class_files, load_command, _("\
|
|||
|
Dynamically load FILE into the running program.\n\
|
|||
|
FILE symbols are recorded for access from GDB.\n\
|
|||
|
Usage: load [FILE] [OFFSET]\n\
|
|||
|
An optional load OFFSET may also be given as a literal address.\n\
|
|||
|
When OFFSET is provided, FILE must also be provided. FILE can be provided\n\
|
|||
|
on its own."), &cmdlist);
|
|||
|
set_cmd_completer (c, filename_completer);
|
|||
|
|
|||
|
add_prefix_cmd ("overlay", class_support, overlay_command,
|
|||
|
_("Commands for debugging overlays."), &overlaylist,
|
|||
|
"overlay ", 0, &cmdlist);
|
|||
|
|
|||
|
add_com_alias ("ovly", "overlay", class_alias, 1);
|
|||
|
add_com_alias ("ov", "overlay", class_alias, 1);
|
|||
|
|
|||
|
add_cmd ("map-overlay", class_support, map_overlay_command,
|
|||
|
_("Assert that an overlay section is mapped."), &overlaylist);
|
|||
|
|
|||
|
add_cmd ("unmap-overlay", class_support, unmap_overlay_command,
|
|||
|
_("Assert that an overlay section is unmapped."), &overlaylist);
|
|||
|
|
|||
|
add_cmd ("list-overlays", class_support, list_overlays_command,
|
|||
|
_("List mappings of overlay sections."), &overlaylist);
|
|||
|
|
|||
|
add_cmd ("manual", class_support, overlay_manual_command,
|
|||
|
_("Enable overlay debugging."), &overlaylist);
|
|||
|
add_cmd ("off", class_support, overlay_off_command,
|
|||
|
_("Disable overlay debugging."), &overlaylist);
|
|||
|
add_cmd ("auto", class_support, overlay_auto_command,
|
|||
|
_("Enable automatic overlay debugging."), &overlaylist);
|
|||
|
add_cmd ("load-target", class_support, overlay_load_command,
|
|||
|
_("Read the overlay mapping state from the target."), &overlaylist);
|
|||
|
|
|||
|
/* Filename extension to source language lookup table: */
|
|||
|
add_setshow_string_noescape_cmd ("extension-language", class_files,
|
|||
|
&ext_args, _("\
|
|||
|
Set mapping between filename extension and source language."), _("\
|
|||
|
Show mapping between filename extension and source language."), _("\
|
|||
|
Usage: set extension-language .foo bar"),
|
|||
|
set_ext_lang_command,
|
|||
|
show_ext_args,
|
|||
|
&setlist, &showlist);
|
|||
|
|
|||
|
add_info ("extensions", info_ext_lang_command,
|
|||
|
_("All filename extensions associated with a source language."));
|
|||
|
|
|||
|
add_setshow_optional_filename_cmd ("debug-file-directory", class_support,
|
|||
|
&debug_file_directory, _("\
|
|||
|
Set the directories where separate debug symbols are searched for."), _("\
|
|||
|
Show the directories where separate debug symbols are searched for."), _("\
|
|||
|
Separate debug symbols are first searched for in the same\n\
|
|||
|
directory as the binary, then in the `" DEBUG_SUBDIRECTORY "' subdirectory,\n\
|
|||
|
and lastly at the path of the directory of the binary with\n\
|
|||
|
each global debug-file-directory component prepended."),
|
|||
|
NULL,
|
|||
|
show_debug_file_directory,
|
|||
|
&setlist, &showlist);
|
|||
|
|
|||
|
add_setshow_enum_cmd ("symbol-loading", no_class,
|
|||
|
print_symbol_loading_enums, &print_symbol_loading,
|
|||
|
_("\
|
|||
|
Set printing of symbol loading messages."), _("\
|
|||
|
Show printing of symbol loading messages."), _("\
|
|||
|
off == turn all messages off\n\
|
|||
|
brief == print messages for the executable,\n\
|
|||
|
and brief messages for shared libraries\n\
|
|||
|
full == print messages for the executable,\n\
|
|||
|
and messages for each shared library."),
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&setprintlist, &showprintlist);
|
|||
|
|
|||
|
add_setshow_boolean_cmd ("separate-debug-file", no_class,
|
|||
|
&separate_debug_file_debug, _("\
|
|||
|
Set printing of separate debug info file search debug."), _("\
|
|||
|
Show printing of separate debug info file search debug."), _("\
|
|||
|
When on, GDB prints the searched locations while looking for separate debug \
|
|||
|
info files."), NULL, NULL, &setdebuglist, &showdebuglist);
|
|||
|
|
|||
|
#if GDB_SELF_TEST
|
|||
|
selftests::register_test
|
|||
|
("filename_language", selftests::filename_language::test_filename_language);
|
|||
|
selftests::register_test
|
|||
|
("set_ext_lang_command",
|
|||
|
selftests::filename_language::test_set_ext_lang_command);
|
|||
|
#endif
|
|||
|
}
|