]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add TOC calculation for XCOFF binary in AIX and remove legacy line number information.
authorAditya Vidyadhar Kamath <aditya.kamath1@ibm.com>
Wed, 7 Jan 2026 09:29:35 +0000 (03:29 -0600)
committerAditya Vidyadhar Kamath <aditya.kamath1@ibm.com>
Wed, 7 Jan 2026 09:29:35 +0000 (03:29 -0600)
Co-authored-by: Simon Marchi <simon.marchi@polymtl.ca>
Closes https://sourceware.org/bugzilla/show_bug.cgi?id=33606

This is a patch to bring back certain XCOFF reading sections back to the GDB code which was removed during the STABS removal.
This patch also removes the legacy line table calculation for STABS since we no longer will support it.

The issue we removed code that will get us the TOC offset in AIX.
This will now cause regressions.

For example,Consider a code where we create a simple library x as below.

int g_in_lib = 777;

int lib_func() {

        return g_in_lib + 1;

}

int lib_func();

Then we use this library X  in main().

int main() {

        printf ("lib_func() = %d \n", lib_func());

        return 0;

}

If we as of today compile master branch in AIX and try to call lib_func() from GDB we get,

GNU gdb (GDB) 18.0.50.20251112-git
Breakpoint 1, main () at //gdb_tests/main.c:5
5           printf ("lib_func() = %d \n", lib_func());

(gdb) call lib_func()

$1 = 536875277

(gdb) q

which is a garbage value instead of 778.

DWARF will not have any information about TOC to maintain uniformity with other operating system.

TOC (Table Of Contents) is a part of XCOFF/AIX ABI and is required for:
1: Loading shared libraries as we need TOC that contain pointers to access global variables and functions entry points.
2: Function calls like the above call where AIX expects register r2 = pointer to TOC which gives fast access to global data plus an ofset
3: Large code model = TOC solves the fact that PPC64 can't embed large 64 bit addresses.

So we need to get GDB to fetch this even though we only read DWARF debug sections in the XCOFF binary. (AIX uses GCC-13 now which produces only DWARF now.).

In the above case since the toc_offset is not there now we cannot access lib_func() causing the regression.

The patch right now brings back the code required to fetch the same. Once this patch is applied we get,

GNU gdb (GDB) 18.0.50.20251204-git
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "powerpc64-ibm-aix7.2.0.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.

+------------------------------------------------------------------------------+
| Find the GDB manual online at:                                               |
| http://www.gnu.org/software/gdb/documentation/.                              |
| For help, type "help".                                                       |
| Type "apropos <word>" to search for commands related to <word>               |
+------------------------------------------------------------------------------+
..
Reading symbols from //gdb_tests/main...
(gdb) b main
Breakpoint 1 at 0x10000530: file //gdb_tests/main.c, line 5.
(gdb) r
Starting program: /gdb_tests/main

Breakpoint 1, main () at //gdb_tests/main.c:5
5           printf ("lib_func() = %d \n", lib_func());
(gdb) call lib_func()
$1 = 778
(gdb) q
A debugging session is active.

        Inferior 1 [process 7340312] will be killed.

Quit anyway? (y or n) y

Also some clean ups of code and additions, they are:
1: Replaced old APIs like bfd_map_over_sections with gdb_bfd_sections() and range-based loops.
2: Used helpers like obstack_strndup instead of manual allocation like changing p = (char *) obstack_alloc (&objfile->objfile_obstack, and strncpy (p, symbol->n_name, E_SYMNMLEN);
to *name = obstack_strndup(&objfile->objfile_obstack, symbol->n_name, E_SYMNMLEN);
3: Removed unused macros as unnecessary global variables as you mentioned
4: Replaced perror_with_name with error() and bfd_errmsg. See: error(_("reading symbol table: %s"), bfd_errmsg(bfd_get_error()));
5: Also used bfd_get_section_alloc_size().
6: Eliminated the xcoff_find_targ_sec_arg struct used in GDB 17 or earlier because it is no longer necessary for context handling.
7: Eliminated the find_targ_sec () used in GDB 17 or earlier since we find the bfd_sect in xcoff_secnum_to_sections().

Approved-By: Tom Tromey <tom@tromey.com>
gdb/xcoffread.c

index 7caa3644b06ebf5641412bc6bffbcb8c8b55c998..66775356960e6b11212e7246f94c45c4fb4a722b 100644 (file)
@@ -88,23 +88,10 @@ struct xcoff_symbol
 
 static bfd *symfile_bfd;
 
-/* Core address of start and end of text of current source file.
-   This is calculated from the first function seen after a C_FILE
-   symbol.  */
-
-
-static CORE_ADDR cur_src_end_addr;
-
 /* Initial symbol-table-debug-string vector length.  */
 
 #define        INITIAL_STABVECTOR_LENGTH       40
 
-/* Size of a COFF symbol.  I think it is always 18, so I'm not sure
-   there is any reason not to just use a #define, but might as well
-   ask BFD for the size and store it here, I guess.  */
-
-static unsigned local_symesz;
-
 struct xcoff_symfile_info
   {
     file_ptr min_lineno_offset {};     /* Where in file lowest line#s are.  */
@@ -163,19 +150,9 @@ static const struct dwarf2_debug_sections dwarf2_xcoff_names = {
   23
 };
 
-static void
-bf_notfound_complaint (void)
-{
-  complaint (_("line numbers off, `.bf' symbol not found"));
-}
-
 static void xcoff_initial_scan (struct objfile *, symfile_add_flags);
 
-static void
-enter_line_range (struct subfile *, unsigned, unsigned,
-                 CORE_ADDR, CORE_ADDR, unsigned *);
-
-static void init_stringtab (bfd *, file_ptr, struct objfile *);
+static void scan_xcoff_symtab (struct objfile *);
 
 static void xcoff_symfile_init (struct objfile *);
 
@@ -183,138 +160,35 @@ static void xcoff_new_init (struct objfile *);
 
 static void xcoff_symfile_finish (struct objfile *);
 
-static void read_symbol (struct internal_syment *, int);
-
-static int read_symbol_lineno (int);
-
-static CORE_ADDR read_symbol_nvalue (int);
-
-static void process_linenos (CORE_ADDR, CORE_ADDR);
-
-struct xcoff_find_targ_sec_arg
-  {
-    int targ_index;
-    int *resultp;
-    asection **bfd_sect;
-    struct objfile *objfile;
-  };
-
-/* Linenos are processed on a file-by-file basis.
-
-   Two reasons:
-
-   1) xlc (IBM's native c compiler) postpones static function code
-   emission to the end of a compilation unit.  This way it can
-   determine if those functions (statics) are needed or not, and
-   can do some garbage collection (I think).  This makes line
-   numbers and corresponding addresses unordered, and we end up
-   with a line table like:
-
-
-   lineno       addr
-   foo()          10    0x100
-   20   0x200
-   30   0x300
-
-   foo3()         70    0x400
-   80   0x500
-   90   0x600
-
-   static foo2()
-   40   0x700
-   50   0x800
-   60   0x900
-
-   and that breaks gdb's binary search on line numbers, if the
-   above table is not sorted on line numbers.  And that sort
-   should be on function based, since gcc can emit line numbers
-   like:
-
-   10   0x100   - for the init/test part of a for stmt.
-   20   0x200
-   30   0x300
-   10   0x400   - for the increment part of a for stmt.
-
-   arrange_linetable() will do this sorting.
+/* Search all BFD sections for the section whose target_index is
+   equal to N_SCNUM.  Set *BFD_SECT to that section.  The section's
+   associated index in the objfile's section_offset table is also
+   stored in *SECNUM.
 
-   2)   aix symbol table might look like:
-
-   c_file               // beginning of a new file
-   .bi          // beginning of include file
-   .ei          // end of include file
-   .bi
-   .ei
-
-   basically, .bi/.ei pairs do not necessarily encapsulate
-   their scope.  They need to be recorded, and processed later
-   on when we come the end of the compilation unit.
-   Include table (inclTable) and process_linenos() handle
-   that.  */
-
-
-/* Given a line table with function entries are marked, arrange its
-   functions in ascending order and strip off function entry markers
-   and return it in a newly created table.  */
-
-/* FIXME: I think all this stuff can be replaced by just passing
-   sort_linevec = 1 to end_compunit_symtab.  */
+   If no match is found, *BFD_SECT is set to NULL, and *SECNUM
+   is set to the text section's number.  */
 
 static void
-arrange_linetable (std::vector<linetable_entry> &old_linetable)
+xcoff_secnum_to_sections (int n_scnum, struct objfile *objfile,
+                         asection **bfd_sect, int *secnum)
 {
-  std::vector<linetable_entry> fentries;
-
-  for (int ii = 0; ii < old_linetable.size (); ++ii)
-    {
-      if (!old_linetable[ii].is_stmt)
-       continue;
-
-      if (old_linetable[ii].line == 0)
-       {
-         /* Function entry found.  */
-         linetable_entry &e = fentries.emplace_back ();
-         e.line = ii;
-         e.is_stmt = true;
-         e.set_unrelocated_pc (old_linetable[ii].unrelocated_pc ());
-       }
-    }
-
-  if (fentries.empty ())
-    return;
+  *bfd_sect = NULL;
+  *secnum = SECT_OFF_TEXT (objfile);
 
-  std::sort (fentries.begin (), fentries.end ());
-
-  /* Allocate a new line table.  */
-  std::vector<linetable_entry> new_linetable;
-  new_linetable.reserve (old_linetable.size ());
-
-  /* If line table does not start with a function beginning, copy up until
-     a function begin.  */
-  for (int i = 0; i < old_linetable.size () && old_linetable[i].line != 0; ++i)
-    new_linetable.push_back (old_linetable[i]);
-
-  /* Now copy function lines one by one.  */
-  for (const linetable_entry &entry : fentries)
+  for (asection *sec : gdb_bfd_sections (objfile->obfd.get ()))
     {
-      /* If the function was compiled with XLC, we may have to add an
-        extra line to cover the function prologue.  */
-      int jj = entry.line;
-      if (jj + 1 < old_linetable.size ()
-         && (old_linetable[jj].unrelocated_pc ()
-             != old_linetable[jj + 1].unrelocated_pc ()))
+      if (sec->target_index == n_scnum)
        {
-         new_linetable.push_back (old_linetable[jj]);
-         new_linetable.back ().line = old_linetable[jj + 1].line;
+         /* This is the section.  Figure out what SECT_OFF_* code it is.  */
+         if (bfd_section_flags (sec) & SEC_CODE)
+           *secnum = SECT_OFF_TEXT (objfile);
+         else if (bfd_section_flags (sec) & SEC_LOAD)
+           *secnum = SECT_OFF_DATA (objfile);
+         else
+           *secnum = gdb_bfd_section_index (objfile->obfd.get (), sec);
+         *bfd_sect = sec;
        }
-
-      for (jj = entry.line + 1;
-          jj < old_linetable.size () && old_linetable[jj].line != 0;
-          ++jj)
-       new_linetable.push_back (old_linetable[jj]);
     }
-
-  new_linetable.shrink_to_fit ();
-  old_linetable = std::move (new_linetable);
 }
 
 /* include file support: C_BINCL/C_EINCL pairs will be kept in the
@@ -349,360 +223,15 @@ static subfile *main_subfile;
    in psymtab to symtab processing.  */
 static legacy_psymtab *this_symtab_psymtab;
 
-/* Objfile related to this_symtab_psymtab; set at the same time.  */
-static struct objfile *this_symtab_objfile;
-
-/* given the start and end addresses of a compilation unit (or a csect,
-   at times) process its lines and create appropriate line vectors.  */
-
-static void
-process_linenos (CORE_ADDR start, CORE_ADDR end)
-{
-  int offset;
-  file_ptr max_offset
-    = XCOFF_DATA (this_symtab_objfile)->max_lineno_offset;
-
-  /* In the main source file, any time we see a function entry, we
-     reset this variable to function's absolute starting line number.
-     All the following line numbers in the function are relative to
-     this, and we record absolute line numbers in record_line().  */
-
-  unsigned int main_source_baseline = 0;
-
-  unsigned *firstLine;
-
-  offset =
-    ((struct xcoff_symloc *) this_symtab_psymtab->read_symtab_private)->lineno_off;
-  if (offset == 0)
-    goto return_after_cleanup;
-
-  if (inclIndx == 0)
-    /* All source lines were in the main source file.  None in include
-       files.  */
-
-    enter_line_range (main_subfile, offset, 0, start, end,
-                     &main_source_baseline);
-
-  else
-    {
-      /* There was source with line numbers in include files.  */
-
-      int linesz =
-       coff_data (this_symtab_objfile->obfd)->local_linesz;
-      main_source_baseline = 0;
-
-      for (int ii = 0; ii < inclIndx; ++ii)
-       {
-         /* If there is main file source before include file, enter it.  */
-         if (offset < inclTable[ii].begin)
-           {
-             enter_line_range
-               (main_subfile, offset, inclTable[ii].begin - linesz,
-                start, 0, &main_source_baseline);
-           }
-
-         if (strcmp (inclTable[ii].name, get_last_source_file ()) == 0)
-           {
-             /* The entry in the include table refers to the main source
-                file.  Add the lines to the main subfile.  */
-
-             main_source_baseline = inclTable[ii].funStartLine;
-             enter_line_range
-               (main_subfile, inclTable[ii].begin, inclTable[ii].end,
-                start, 0, &main_source_baseline);
-             inclTable[ii].subfile = main_subfile;
-           }
-         else
-           {
-             /* Have a new subfile for the include file.  */
-             inclTable[ii].subfile = new subfile;
-
-             firstLine = &(inclTable[ii].funStartLine);
-
-             /* Enter include file's lines now.  */
-             enter_line_range (inclTable[ii].subfile, inclTable[ii].begin,
-                               inclTable[ii].end, start, 0, firstLine);
-           }
-
-         if (offset <= inclTable[ii].end)
-           offset = inclTable[ii].end + linesz;
-       }
-
-      /* All the include files' line have been processed at this point.  Now,
-        enter remaining lines of the main file, if any left.  */
-      if (offset < max_offset + 1 - linesz)
-       {
-         enter_line_range (main_subfile, offset, 0, start, end,
-                           &main_source_baseline);
-       }
-    }
-
-  /* Process main file's line numbers.  */
-  if (!main_subfile->line_vector_entries.empty ())
-    {
-      /* Line numbers are not necessarily ordered.  xlc compilation will
-        put static function to the end.  */
-      arrange_linetable (main_subfile->line_vector_entries);
-    }
-
-  /* Now, process included files' line numbers.  */
-
-  for (int ii = 0; ii < inclIndx; ++ii)
-    {
-      if (inclTable[ii].subfile != main_subfile
-         && !inclTable[ii].subfile->line_vector_entries.empty ())
-       {
-         /* Line numbers are not necessarily ordered.  xlc compilation will
-            put static function to the end.  */
-         arrange_linetable (inclTable[ii].subfile->line_vector_entries);
-
-         push_subfile ();
-
-         /* For the same include file, we might want to have more than one
-            subfile.  This happens if we have something like:
-
-            ......
-            #include "foo.h"
-            ......
-            #include "foo.h"
-            ......
-
-            while foo.h including code in it.  (stupid but possible)
-            Since start_subfile() looks at the name and uses an
-            existing one if finds, we need to provide a fake name and
-            fool it.  */
-
-#if 0
-         start_subfile (inclTable[ii].name);
-#else
-         {
-           /* Pick a fake name that will produce the same results as this
-              one when passed to deduce_language_from_filename.  Kludge on
-              top of kludge.  */
-           const char *fakename = strrchr (inclTable[ii].name, '.');
-
-           if (fakename == NULL)
-             fakename = " ?";
-           start_subfile (fakename);
-         }
-         struct subfile *current_subfile = get_current_subfile ();
-         current_subfile->name = inclTable[ii].name;
-         current_subfile->name_for_id = inclTable[ii].name;
-#endif
-
-         start_subfile (pop_subfile ());
-       }
-    }
-
-return_after_cleanup:
-
-  /* We don't want to keep alloc/free'ing the global include file table.  */
-  inclIndx = 0;
-}
-
 static void
 aix_process_linenos (struct objfile *objfile)
 {
   /* There is no linenos to read if there are only dwarf info.  */
   if (this_symtab_psymtab == NULL)
     return;
-
-  /* Process line numbers and enter them into line vector.  */
-  process_linenos (get_last_source_start_addr (), cur_src_end_addr);
-}
-
-
-/* Enter a given range of lines into the line vector.
-   can be called in the following two ways:
-   enter_line_range (subfile, beginoffset, endoffset,
-                    startaddr, 0, firstLine)  or
-   enter_line_range (subfile, beginoffset, 0,
-                    startaddr, endaddr, firstLine)
-
-   endoffset points to the last line table entry that we should pay
-   attention to.  */
-
-static void
-enter_line_range (struct subfile *subfile, unsigned beginoffset,
-                 unsigned endoffset,   /* offsets to line table */
-                 CORE_ADDR startaddr,  /* offsets to line table */
-                 CORE_ADDR endaddr, unsigned *firstLine)
-{
-  struct objfile *objfile = this_symtab_objfile;
-  struct gdbarch *gdbarch = objfile->arch ();
-  unsigned int curoffset;
-  CORE_ADDR addr;
-  void *ext_lnno;
-  struct internal_lineno int_lnno;
-  unsigned int limit_offset;
-  bfd *abfd;
-  int linesz;
-
-  if (endoffset == 0 && startaddr == 0 && endaddr == 0)
-    return;
-  curoffset = beginoffset;
-  limit_offset = XCOFF_DATA (objfile)->max_lineno_offset;
-
-  if (endoffset != 0)
-    {
-      if (endoffset >= limit_offset)
-       {
-         complaint (_("Bad line table offset in C_EINCL directive"));
-         return;
-       }
-      limit_offset = endoffset;
-    }
-  else
-    limit_offset -= 1;
-
-  abfd = objfile->obfd.get ();
-  linesz = coff_data (abfd)->local_linesz;
-  ext_lnno = alloca (linesz);
-
-  while (curoffset <= limit_offset)
-    {
-      if (bfd_seek (abfd, curoffset, SEEK_SET) != 0
-         || bfd_read (ext_lnno, linesz, abfd) != linesz)
-       return;
-      bfd_coff_swap_lineno_in (abfd, ext_lnno, &int_lnno);
-
-      /* Find the address this line represents.  */
-      addr = (int_lnno.l_lnno
-             ? int_lnno.l_addr.l_paddr
-             : read_symbol_nvalue (int_lnno.l_addr.l_symndx));
-      addr += objfile->text_section_offset ();
-
-      if (addr < startaddr || (endaddr && addr >= endaddr))
-       return;
-
-      CORE_ADDR record_addr = (gdbarch_addr_bits_remove (gdbarch, addr)
-                              - objfile->text_section_offset ());
-      if (int_lnno.l_lnno == 0)
-       {
-         *firstLine = read_symbol_lineno (int_lnno.l_addr.l_symndx);
-         record_line (subfile, 0, unrelocated_addr (record_addr));
-         --(*firstLine);
-       }
-      else
-       record_line (subfile, *firstLine + int_lnno.l_lnno,
-                    unrelocated_addr (record_addr));
-      curoffset += linesz;
-    }
 }
 
 
-/* Save the vital information for use when closing off the current file.
-   NAME is the file name the symbols came from, START_ADDR is the first
-   text address for the file, and SIZE is the number of bytes of text.  */
-
-#define complete_symtab(name, start_addr) {    \
-  set_last_source_file (name);                 \
-  set_last_source_start_addr (start_addr);     \
-}
-
-
-#define        SYMNAME_ALLOC(NAME, ALLOCED)    \
-  ((ALLOCED) ? (NAME) : obstack_strdup (&objfile->objfile_obstack, \
-                                       (NAME)))
-
-/* Set *SYMBOL to symbol number symno in symtbl.  */
-static void
-read_symbol (struct internal_syment *symbol, int symno)
-{
-  struct xcoff_symfile_info *xcoff = XCOFF_DATA (this_symtab_objfile);
-  int nsyms = xcoff->symtbl_num_syms;
-  char *stbl = xcoff->symtbl;
-
-  if (symno < 0 || symno >= nsyms)
-    {
-      complaint (_("Invalid symbol offset"));
-      symbol->n_value = 0;
-      symbol->n_scnum = -1;
-      return;
-    }
-  bfd_coff_swap_sym_in (this_symtab_objfile->obfd.get (),
-                       stbl + (symno * local_symesz),
-                       symbol);
-}
-
-/* Get value corresponding to symbol number symno in symtbl.  */
-
-static CORE_ADDR
-read_symbol_nvalue (int symno)
-{
-  struct internal_syment symbol[1];
-
-  read_symbol (symbol, symno);
-  return symbol->n_value;
-}
-
-
-/* Find the address of the function corresponding to symno, where
-   symno is the symbol pointed to by the linetable.  */
-
-static int
-read_symbol_lineno (int symno)
-{
-  struct objfile *objfile = this_symtab_objfile;
-  int xcoff64 = bfd_xcoff_is_xcoff64 (objfile->obfd);
-
-  struct xcoff_symfile_info *info = XCOFF_DATA (objfile);
-  int nsyms = info->symtbl_num_syms;
-  char *stbl = info->symtbl;
-  char *strtbl = info->strtbl;
-
-  struct internal_syment symbol[1];
-  union internal_auxent main_aux[1];
-
-  if (symno < 0)
-    {
-      bf_notfound_complaint ();
-      return 0;
-    }
-
-  /* Note that just searching for a short distance (e.g. 50 symbols)
-     is not enough, at least in the following case.
-
-     .extern foo
-     [many .stabx entries]
-     [a few functions, referring to foo]
-     .globl foo
-     .bf
-
-     What happens here is that the assembler moves the .stabx entries
-     to right before the ".bf" for foo, but the symbol for "foo" is before
-     all the stabx entries.  See PR gdb/2222.  */
-
-  /* Maintaining a table of .bf entries might be preferable to this search.
-     If I understand things correctly it would need to be done only for
-     the duration of a single psymtab to symtab conversion.  */
-  while (symno < nsyms)
-    {
-      bfd_coff_swap_sym_in (symfile_bfd,
-                           stbl + (symno * local_symesz), symbol);
-      if (symbol->n_sclass == C_FCN)
-       {
-         char *name = xcoff64 ? strtbl + symbol->n_offset : symbol->n_name;
-
-         if (strcmp (name, ".bf") == 0)
-           goto gotit;
-       }
-      symno += symbol->n_numaux + 1;
-    }
-
-  bf_notfound_complaint ();
-  return 0;
-
-gotit:
-  /* Take aux entry and return its lineno.  */
-  symno++;
-  bfd_coff_swap_aux_in (objfile->obfd.get (), stbl + symno * local_symesz,
-                       symbol->n_type, symbol->n_sclass,
-                       0, symbol->n_numaux, main_aux);
-
-  return main_aux->x_sym.x_misc.x_lnsz.x_lnno;
-}
-
 /* Support for line number handling.  */
 
 /* This function is called for every section; it finds the outer limits
@@ -769,53 +298,158 @@ xcoff_symfile_finish (struct objfile *objfile)
   inclIndx = inclLength = inclDepth = 0;
 }
 
+/* Swap raw symbol at *RAW and put the name in *NAME, the symbol in
+   *SYMBOL, the first auxent in *AUX.  Advance *RAW and *SYMNUMP over
+   the symbol and its auxents.  */
 
 static void
-init_stringtab (bfd *abfd, file_ptr offset, struct objfile *objfile)
+swap_sym (struct internal_syment *symbol, union internal_auxent *aux,
+         const char **name, char **raw, unsigned int *symnump,
+         struct objfile *objfile)
 {
-  long length;
-  int val;
-  unsigned char lengthbuf[4];
-  char *strtbl;
-  struct xcoff_symfile_info *xcoff = XCOFF_DATA (objfile);
+  bfd_coff_swap_sym_in (objfile->obfd.get (), *raw, symbol);
+  if (symbol->n_zeroes)
+    {
+      /* If it's exactly E_SYMNMLEN characters long it isn't
+        '\0'-terminated.  */
+      if (symbol->n_name[E_SYMNMLEN - 1] != '\0')
+       {
+         /* FIXME: wastes memory for symbols which we don't end up putting
+            into the minimal symbols.  */
+         *name = obstack_strndup (&objfile->objfile_obstack,
+                                   symbol->n_name, E_SYMNMLEN);
+       }
+      else
+       /* Point to the unswapped name as that persists as long as the
+          objfile does.  */
+       *name = ((struct external_syment *) *raw)->e.e_name;
+    }
+  else if (symbol->n_sclass & 0x80)
+    {
+      *name = XCOFF_DATA (objfile)->debugsec + symbol->n_offset;
+    }
+  else
+    {
+      *name = XCOFF_DATA (objfile)->strtbl + symbol->n_offset;
+    }
+  ++*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);
 
-  xcoff->strtbl = NULL;
+      *symnump += symbol->n_numaux;
+      *raw += coff_data (objfile->obfd)->local_symesz * symbol->n_numaux;
+    }
+}
 
-  if (bfd_seek (abfd, offset, SEEK_SET) < 0)
-    error (_("cannot seek to string table in %s: %s"),
-          bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ()));
+static void
+scan_xcoff_symtab (struct objfile *objfile)
+{
+  CORE_ADDR toc_offset = 0;    /* toc offset value in data section.  */
 
-  val = bfd_read ((char *) lengthbuf, sizeof lengthbuf, abfd);
-  length = bfd_h_get_32 (abfd, lengthbuf);
+  const char *namestring;
+  bfd *abfd;
+  asection *bfd_sect = nullptr;
+  int ignored;
+  unsigned int nsyms;
 
-  /* If no string table is needed, then the file may end immediately
-     after the symbols.  Just return with `strtbl' set to NULL.  */
+  char *sraw_symbol;
+  struct internal_syment symbol;
+  union internal_auxent main_aux[5];
+  unsigned int ssymnum;
 
-  if (val != sizeof lengthbuf || length < sizeof lengthbuf)
-    return;
+  set_last_source_file (NULL);
 
-  /* Allocate string table from objfile_obstack.  We will need this table
-     as long as we have its symbol table around.  */
+  abfd = objfile->obfd.get ();
+
+  sraw_symbol = XCOFF_DATA (objfile)->symtbl;
+  nsyms = XCOFF_DATA (objfile)->symtbl_num_syms;
+  ssymnum = 0;
+  while (ssymnum < nsyms)
+    {
+      int sclass;
 
-  strtbl = (char *) obstack_alloc (&objfile->objfile_obstack, length);
-  xcoff->strtbl = strtbl;
+      QUIT;
 
-  /* Copy length buffer, the first byte is usually zero and is
-     used for stabs with a name length of zero.  */
-  memcpy (strtbl, lengthbuf, sizeof lengthbuf);
-  if (length == sizeof lengthbuf)
-    return;
+      bfd_coff_swap_sym_in (abfd, sraw_symbol, &symbol);
+      sclass = symbol.n_sclass;
 
-  val = bfd_read (strtbl + sizeof lengthbuf, length - sizeof lengthbuf, abfd);
+      switch (sclass)
+       {
+       case C_HIDEXT:
+         {
+           /* The CSECT auxent--always the last auxent.  */
+           union internal_auxent csect_aux;
+
+           swap_sym (&symbol, &main_aux[0], &namestring, &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];
+
+           switch (csect_aux.x_csect.x_smtyp & 0x7)
+             {
+             case XTY_SD:
+               switch (csect_aux.x_csect.x_smclas)
+                 {
+                 case XMC_TC0:
+                   if (toc_offset)
+                     warning (_("More than one XMC_TC0 symbol found."));
+                   toc_offset = symbol.n_value;
+
+                   /* Make TOC offset relative to start address of
+                      section.  */
+                   xcoff_secnum_to_sections (symbol.n_scnum, objfile,
+                                                        &bfd_sect, &ignored);
+                   if (bfd_sect)
+                     toc_offset -= bfd_section_vma (bfd_sect);
+                   break;
+
+                 default:
+                   break;
+                 }
+               break;
+             }
+         }
+         break;
+       default:
+         {
+           complaint (_("Storage class %d not recognized during scan"),
+                      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;
+       }
+    }
 
-  if (val != length - sizeof lengthbuf)
-    error (_("cannot read string table from %s: %s"),
-          bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ()));
-  if (strtbl[length - 1] != '\0')
-    error (_("bad symbol file: string table "
-            "does not end with null character"));
+  /* 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.  */
 
-  return;
+  XCOFF_DATA (objfile)->toc_offset = toc_offset;
 }
 
 /* Return the toc offset value for a given objfile.  */
@@ -841,39 +475,77 @@ static void
 xcoff_initial_scan (struct objfile *objfile, symfile_add_flags symfile_flags)
 {
   bfd *abfd;
+  int val;
   int num_symbols;             /* # of symbols */
   file_ptr symtab_offset;      /* symbol table and */
-  file_ptr stringtab_offset;   /* string table file offsets */
   struct xcoff_symfile_info *info;
+  const char *name;
+  unsigned int size;
 
   info = XCOFF_DATA (objfile);
   symfile_bfd = abfd = objfile->obfd.get ();
+  name = objfile_name (objfile);
 
   num_symbols = bfd_get_symcount (abfd);       /* # of symbols */
   symtab_offset = obj_sym_filepos (abfd);      /* symbol table file offset */
-  stringtab_offset = symtab_offset +
-    num_symbols * coff_data (abfd)->local_symesz;
 
   info->min_lineno_offset = 0;
   info->max_lineno_offset = 0;
-  info->debugsec = nullptr;
-  bfd_map_over_sections (abfd, find_linenos, info);
+  for (asection *sec : gdb_bfd_sections (abfd))
+    find_linenos (abfd, sec, info);
 
   if (num_symbols > 0)
     {
-      /* Read the string table.  */
-      init_stringtab (abfd, stringtab_offset, objfile);
+      /* Read the .debug section, if present and if we're not ignoring
+        it.  */
+      if (!(objfile->flags & OBJF_READNEVER))
+       {
+         struct bfd_section *secp;
+         bfd_size_type length;
+         bfd_byte *debugsec = NULL;
+
+         secp = bfd_get_section_by_name (abfd, ".debug");
+         if (secp)
+           {
+             length = bfd_get_section_alloc_size (abfd, secp);
+             if (length)
+               {
+                 debugsec
+                   = (bfd_byte *) obstack_alloc (&objfile->objfile_obstack,
+                                                 length);
+
+                 if (!bfd_get_full_section_contents (abfd, secp, &debugsec))
+                   {
+                     error (_("Error reading .debug section of `%s': %s"),
+                            name, bfd_errmsg (bfd_get_error ()));
+                   }
+               }
+           }
+         info->debugsec = (char *) debugsec;
+       }
     }
 
+  /* Read the symbols.  We keep them in core because we will want to
+     access them randomly in read_symbol*.  */
+  val = bfd_seek (abfd, symtab_offset, SEEK_SET);
+  if (val < 0)
+    error (_("Error reading symbols from %s: %s"),
+          name, bfd_errmsg (bfd_get_error ()));
+  size = coff_data (abfd)->local_symesz * num_symbols;
+  info->symtbl = (char *) obstack_alloc (&objfile->objfile_obstack, size);
+  info->symtbl_num_syms = num_symbols;
+
+  val = bfd_read (info->symtbl, size, abfd);
+  if (val != size)
+    error (_("reading symbol table: %s"), bfd_errmsg (bfd_get_error ()));
+
+  /* We need to do this to get the TOC information only.  STABS
+     format is no longer supported.  */
+  scan_xcoff_symtab (objfile);
+
   /* DWARF2 sections.  */
 
-  if (!(objfile->flags & OBJF_READNEVER))
-    {
-      /* If we can't read dwarf from an inferior, it will only have
-        stabs debuginfo, which we don't support anymore.  */
-      if (!dwarf2_initialize_objfile (objfile, &dwarf2_xcoff_names))
-         warning (_("No usable debug information found."));
-    }
+  dwarf2_initialize_objfile (objfile, &dwarf2_xcoff_names);
 }
 \f
 static void