| /* 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 sysmbols 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); |
| } |