mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
441 lines
13 KiB
C
441 lines
13 KiB
C
/* Read AIX xcoff symbol tables and convert to internal format, for GDB.
|
|
Copyright (C) 1986-2026 Free Software Foundation, Inc.
|
|
Derived from coffread.c, dbxread.c, and a lot of hacking.
|
|
Contributed by IBM Corporation.
|
|
|
|
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 "bfd.h"
|
|
#include "event-top.h"
|
|
|
|
#include "coff/internal.h"
|
|
#include "libcoff.h"
|
|
#include "coff/xcoff.h"
|
|
#include "coff/rs6000.h"
|
|
#include "xcoffread.h"
|
|
|
|
#include "symtab.h"
|
|
#include "minsyms.h"
|
|
#include "gdbtypes.h"
|
|
#include "symfile.h"
|
|
#include "objfiles.h"
|
|
#include "complaints.h"
|
|
#include "dwarf2/sect-names.h"
|
|
#include "dwarf2/public.h"
|
|
#include "cli/cli-style.h"
|
|
|
|
struct xcoff_symfile_info
|
|
{
|
|
/* Offset in data section to TOC anchor. */
|
|
CORE_ADDR toc_offset = 0;
|
|
};
|
|
|
|
/* Key for XCOFF-associated data. */
|
|
|
|
static const registry<objfile>::key<xcoff_symfile_info> xcoff_objfile_data_key;
|
|
|
|
/* XCOFF names for dwarf sections. There is no compressed sections. */
|
|
|
|
static const dwarf2_debug_sections dwarf2_xcoff_names = {
|
|
{ ".dwinfo", NULL },
|
|
{ ".dwabrev", NULL },
|
|
{ ".dwline", NULL },
|
|
{ ".dwloc", NULL },
|
|
{ NULL, NULL }, /* debug_loclists */
|
|
/* AIX XCOFF defines one, named DWARF section for macro debug information.
|
|
XLC does not generate debug_macinfo for DWARF4 and below.
|
|
The section is assigned to debug_macro for DWARF5 and above. */
|
|
{ NULL, NULL },
|
|
{ ".dwmac", NULL },
|
|
{ ".dwstr", NULL },
|
|
{ NULL, NULL }, /* debug_str_offsets */
|
|
{ NULL, NULL }, /* debug_line_str */
|
|
{ ".dwrnges", NULL },
|
|
{ NULL, NULL }, /* debug_rnglists */
|
|
{ ".dwpbtyp", NULL },
|
|
{ NULL, NULL }, /* debug_addr */
|
|
{ ".dwframe", NULL },
|
|
{ NULL, NULL }, /* eh_frame */
|
|
{ NULL, NULL }, /* gdb_index */
|
|
{ NULL, NULL }, /* debug_names */
|
|
{ NULL, NULL }, /* debug_aranges */
|
|
23
|
|
};
|
|
|
|
/* Search all BFD sections for the section whose target_index is
|
|
equal to N_SCNUM.
|
|
|
|
If no match is found, return nullptr. */
|
|
|
|
static asection *
|
|
xcoff_secnum_to_section (int n_scnum, objfile *objfile)
|
|
{
|
|
for (asection *sec : gdb_bfd_sections (objfile->obfd.get ()))
|
|
if (sec->target_index == n_scnum)
|
|
return sec;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* Do initialization in preparation for reading symbols from OBJFILE.
|
|
|
|
We will only be called if this is an XCOFF or XCOFF-like file.
|
|
BFD handles figuring out the format of the file, and code in symfile.c
|
|
uses BFD's determination to vector to us. */
|
|
|
|
static void
|
|
xcoff_symfile_init (objfile *objfile)
|
|
{
|
|
/* Allocate struct to keep track of the symfile. */
|
|
xcoff_objfile_data_key.emplace (objfile);
|
|
}
|
|
|
|
/* Swap raw symbol at *RAW. Put the symbol in *SYMBOL and the first auxent in
|
|
*AUX. Advance *RAW and *SYMNUMP over the symbol and its auxents. */
|
|
|
|
static void
|
|
swap_sym (struct internal_syment *symbol, union internal_auxent *aux,
|
|
char **raw, unsigned int *symnump, struct objfile *objfile)
|
|
{
|
|
bfd_coff_swap_sym_in (objfile->obfd.get (), *raw, symbol);
|
|
|
|
++*symnump;
|
|
*raw += coff_data (objfile->obfd)->local_symesz;
|
|
if (symbol->n_numaux > 0)
|
|
{
|
|
bfd_coff_swap_aux_in (objfile->obfd.get (), *raw, symbol->n_type,
|
|
symbol->n_sclass, 0, symbol->n_numaux, aux);
|
|
|
|
*symnump += symbol->n_numaux;
|
|
*raw += coff_data (objfile->obfd)->local_symesz * symbol->n_numaux;
|
|
}
|
|
}
|
|
|
|
/* Locate the TOC offset in the XCOFF symbol table and assign
|
|
xcoff_symfile_info::toc_offset. */
|
|
|
|
static void
|
|
xcoff_find_toc_offset (objfile *objfile)
|
|
{
|
|
bfd *abfd = objfile->obfd.get ();
|
|
file_ptr symtab_offset = obj_sym_filepos (abfd);
|
|
|
|
/* Seek to symbol table location. */
|
|
if (bfd_seek (abfd, symtab_offset, SEEK_SET) < 0)
|
|
error (_("Error reading symbols from %ps: %s"),
|
|
styled_string (file_name_style.style (), objfile_name (objfile)),
|
|
bfd_errmsg (bfd_get_error ()));
|
|
|
|
unsigned int num_symbols = bfd_get_symcount (abfd);
|
|
size_t size = coff_data (abfd)->local_symesz * num_symbols;
|
|
gdb::char_vector symtbl (size);
|
|
|
|
/* Read in symbol table. */
|
|
if (int ret = bfd_read (symtbl.data (), size, abfd);
|
|
ret != size)
|
|
error (_("reading symbol table: %s"), bfd_errmsg (bfd_get_error ()));
|
|
|
|
char *sraw_symbol = symtbl.data ();
|
|
CORE_ADDR toc_offset = 0;
|
|
for (unsigned int ssymnum = 0; ssymnum < num_symbols; )
|
|
{
|
|
QUIT;
|
|
|
|
internal_syment symbol;
|
|
bfd_coff_swap_sym_in (abfd, sraw_symbol, &symbol);
|
|
|
|
switch (symbol.n_sclass)
|
|
{
|
|
case C_HIDEXT:
|
|
{
|
|
/* The CSECT auxent--always the last auxent. */
|
|
internal_auxent csect_aux;
|
|
internal_auxent main_aux[5];
|
|
|
|
swap_sym (&symbol, &main_aux[0], &sraw_symbol, &ssymnum, objfile);
|
|
if (symbol.n_numaux > 1)
|
|
{
|
|
bfd_coff_swap_aux_in
|
|
(objfile->obfd.get (),
|
|
sraw_symbol - coff_data (abfd)->local_symesz,
|
|
symbol.n_type,
|
|
symbol.n_sclass,
|
|
symbol.n_numaux - 1,
|
|
symbol.n_numaux,
|
|
&csect_aux);
|
|
}
|
|
else
|
|
csect_aux = main_aux[0];
|
|
|
|
if ((csect_aux.x_csect.x_smtyp & 0x7) == XTY_SD
|
|
&& csect_aux.x_csect.x_smclas == XMC_TC0)
|
|
{
|
|
if (toc_offset != 0)
|
|
warning (_("More than one XMC_TC0 symbol found."));
|
|
|
|
toc_offset = symbol.n_value;
|
|
|
|
/* Make TOC offset relative to start address of section. */
|
|
asection *bfd_sect
|
|
= xcoff_secnum_to_section (symbol.n_scnum, objfile);
|
|
if (bfd_sect != nullptr)
|
|
toc_offset -= bfd_section_vma (bfd_sect);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
complaint (_("Storage class %d not recognized during scan"),
|
|
symbol.n_sclass);
|
|
[[fallthrough]];
|
|
|
|
case C_RSYM:
|
|
{
|
|
/* We probably could save a few instructions by assuming that
|
|
C_LSYM, C_PSYM, etc., never have auxents. */
|
|
int naux1 = symbol.n_numaux + 1;
|
|
|
|
ssymnum += naux1;
|
|
sraw_symbol += bfd_coff_symesz (abfd) * naux1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Record the toc offset value of this symbol table into objfile
|
|
structure. If no XMC_TC0 is found, toc_offset should be zero.
|
|
Another place to obtain this information would be file auxiliary
|
|
header. */
|
|
|
|
xcoff_objfile_data_key.get (objfile)->toc_offset = toc_offset;
|
|
}
|
|
|
|
/* Return the toc offset value for a given objfile. */
|
|
|
|
CORE_ADDR
|
|
xcoff_get_toc_offset (objfile *objfile)
|
|
{
|
|
if (objfile != nullptr)
|
|
return xcoff_objfile_data_key.get (objfile)->toc_offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function is used to read minimal symbols from the XCOFF STABS
|
|
symbol table. This function needs to be called when a library (like
|
|
libc) in AIX is compiled using xlc whose debug format is STABS.
|
|
When STABS debug information is not available, then in the backtrace
|
|
or other GDB features the STABS compiled functions like (printf)
|
|
will not be seen. Though we no longer support STABS debug, this
|
|
function records minimal symbol or information needed to show
|
|
such functions in backtrace. */
|
|
|
|
static void
|
|
read_xcoff_minimal_symbols (objfile *objfile)
|
|
{
|
|
bfd *abfd = objfile->obfd.get ();
|
|
|
|
gdb::array_view<asymbol *> symbol_table
|
|
= gdb_bfd_canonicalize_symtab (abfd, false);
|
|
/* Get the number of symbols we need. */
|
|
int number_of_symbols = symbol_table.size ();
|
|
|
|
/* Return on error. */
|
|
if (number_of_symbols == 0)
|
|
return;
|
|
|
|
minimal_symbol_reader reader (objfile);
|
|
for (int i = 0; i < number_of_symbols; i++)
|
|
{
|
|
asymbol *sym = symbol_table[i];
|
|
flagword sym_flags = sym->flags;
|
|
CORE_ADDR sym_value = bfd_asymbol_value (sym);
|
|
asection *sym_section = bfd_asymbol_section (sym);
|
|
enum minimal_symbol_type ms_type = mst_unknown;
|
|
const char *sym_name = bfd_asymbol_name (sym);
|
|
|
|
/* Skip undefined sections if any. */
|
|
if (sym_section == bfd_und_section_ptr)
|
|
continue;
|
|
|
|
/* Skip undefined, special symbols and debugging symbols. */
|
|
if (sym_flags & (BSF_DEBUGGING | BSF_SECTION_SYM))
|
|
continue;
|
|
|
|
/* We need to pass ms_type when we record. So find the symbol type. */
|
|
|
|
/* If this is an object file. */
|
|
if (sym_flags & BSF_OBJECT)
|
|
{
|
|
/* Code section. */
|
|
if (sym_section && (bfd_section_flags (sym_section) & SEC_CODE))
|
|
ms_type = mst_text;
|
|
/* Data section. */
|
|
else if (sym_section && (bfd_section_flags (sym_section) & SEC_DATA))
|
|
ms_type = mst_data;
|
|
/* BSS section. */
|
|
else if (sym_section && (bfd_section_flags (sym_section) & SEC_ALLOC))
|
|
ms_type = mst_bss;
|
|
else
|
|
ms_type = mst_unknown;
|
|
}
|
|
/* Check for symbols marked as functions explicitly. */
|
|
else if (sym_flags & BSF_FUNCTION)
|
|
ms_type = mst_text;
|
|
/* Section Based Guess. Code or Data or BSS. */
|
|
else if (sym_section)
|
|
{
|
|
if (bfd_section_flags (sym_section) & SEC_CODE)
|
|
ms_type = mst_text;
|
|
else if (bfd_section_flags (sym_section) & SEC_DATA)
|
|
ms_type = mst_data;
|
|
else if (bfd_section_flags (sym_section) & SEC_ALLOC)
|
|
ms_type = mst_bss;
|
|
}
|
|
|
|
/* Add the symbol if it's global or weak */
|
|
if ((sym_flags & (BSF_GLOBAL | BSF_WEAK)) && sym_name && *sym_name)
|
|
reader.record_with_info (sym_name, unrelocated_addr (sym_value), ms_type,
|
|
gdb_bfd_section_index (abfd, sym_section));
|
|
}
|
|
|
|
reader.install ();
|
|
}
|
|
|
|
/* Read the XCOFF symbol table. The only thing we are interested in is the TOC
|
|
offset value. */
|
|
|
|
static void
|
|
xcoff_symfile_read (objfile *objfile, symfile_add_flags symfile_flags)
|
|
{
|
|
xcoff_find_toc_offset (objfile);
|
|
|
|
/* Read minimal symbols from the STABS symbol table of an XCOFF binary
|
|
This makes sure we see functions from libc given libraries link to
|
|
it and libc in AIX may not be DWARF compiled. . */
|
|
read_xcoff_minimal_symbols (objfile);
|
|
|
|
/* DWARF2 sections. */
|
|
dwarf2_initialize_objfile (objfile, &dwarf2_xcoff_names);
|
|
}
|
|
|
|
static void
|
|
xcoff_symfile_offsets (objfile *objfile, const section_addr_info &addrs)
|
|
{
|
|
default_symfile_offsets (objfile, addrs);
|
|
|
|
/* Oneof the weird side-effects of default_symfile_offsets is that
|
|
it sometimes sets some section indices to zero for sections that,
|
|
in fact do not exist. See the body of default_symfile_offsets
|
|
for more info on when that happens. Undo that, as this then allows
|
|
us to test whether the associated section exists or not, and then
|
|
access it quickly (without searching it again). */
|
|
|
|
if (objfile->section_offsets.empty ())
|
|
return; /* Is that even possible? Better safe than sorry. */
|
|
|
|
const char *first_section_name
|
|
= bfd_section_name (objfile->sections_start[0].the_bfd_section);
|
|
|
|
if (objfile->sect_index_text == 0 && !streq (first_section_name, ".text"))
|
|
objfile->sect_index_text = -1;
|
|
|
|
if (objfile->sect_index_data == 0 && !streq (first_section_name, ".data"))
|
|
objfile->sect_index_data = -1;
|
|
|
|
if (objfile->sect_index_bss == 0 && !streq (first_section_name, ".bss"))
|
|
objfile->sect_index_bss = -1;
|
|
|
|
if (objfile->sect_index_rodata == 0
|
|
&& !streq (first_section_name, ".rodata"))
|
|
objfile->sect_index_rodata = -1;
|
|
}
|
|
|
|
/* Register our ability to parse symbols for xcoff BFD files. */
|
|
|
|
static const sym_fns xcoff_sym_fns =
|
|
{
|
|
xcoff_symfile_init, /* read initial info, setup for sym_read() */
|
|
xcoff_symfile_read, /* read a symbol file into symtab */
|
|
xcoff_symfile_offsets, /* xlate offsets ext->int form */
|
|
default_symfile_segments, /* Get segment information from a file. */
|
|
default_symfile_relocate, /* Relocate a debug section. */
|
|
NULL, /* sym_probe_fns */
|
|
};
|
|
|
|
/* Same as xcoff_get_n_import_files, but for core files. */
|
|
|
|
static int
|
|
xcoff_get_core_n_import_files (bfd *abfd)
|
|
{
|
|
asection *sect = bfd_get_section_by_name (abfd, ".ldinfo");
|
|
if (sect == NULL)
|
|
return -1; /* Not a core file. */
|
|
|
|
int n_entries = 0;
|
|
for (file_ptr offset = 0; offset < bfd_section_size (sect);)
|
|
{
|
|
n_entries++;
|
|
|
|
gdb_byte buf[4];
|
|
if (!bfd_get_section_contents (abfd, sect, buf, offset, 4))
|
|
return -1;
|
|
|
|
int next = bfd_get_32 (abfd, buf);
|
|
if (next == 0)
|
|
break; /* This is the last entry. */
|
|
|
|
offset += next;
|
|
}
|
|
|
|
/* Return the number of entries, excluding the first one, which is
|
|
the path to the executable that produced this core file. */
|
|
return n_entries - 1;
|
|
}
|
|
|
|
/* Return the number of import files (shared libraries) that the given
|
|
BFD depends on. Return -1 if this number could not be computed. */
|
|
|
|
int
|
|
xcoff_get_n_import_files (bfd *abfd)
|
|
{
|
|
asection *sect = bfd_get_section_by_name (abfd, ".loader");
|
|
|
|
/* If the ".loader" section does not exist, the objfile is probably
|
|
not an executable. Might be a core file... */
|
|
if (sect == NULL)
|
|
return xcoff_get_core_n_import_files (abfd);
|
|
|
|
/* The number of entries in the Import Files Table is stored in
|
|
field l_nimpid. This field is always at offset 16, and is
|
|
always 4 bytes long. Read those 4 bytes. */
|
|
gdb_byte buf[4];
|
|
if (!bfd_get_section_contents (abfd, sect, buf, 16, 4))
|
|
return -1;
|
|
|
|
int l_nimpid = bfd_get_32 (abfd, buf);
|
|
|
|
/* By convention, the first entry is the default LIBPATH value
|
|
to be used by the system loader, so it does not count towards
|
|
the number of import files. */
|
|
return l_nimpid - 1;
|
|
}
|
|
|
|
INIT_GDB_FILE (xcoffread)
|
|
{
|
|
add_symtab_fns (bfd_target_xcoff_flavour, &xcoff_sym_fns);
|
|
}
|