|  | /* Handle TIC6X (DSBT) shared libraries for GDB, the GNU Debugger. | 
|  | Copyright (C) 2010-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | 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 "inferior.h" | 
|  | #include "gdbcore.h" | 
|  | #include "solib.h" | 
|  | #include "solist.h" | 
|  | #include "objfiles.h" | 
|  | #include "symtab.h" | 
|  | #include "language.h" | 
|  | #include "command.h" | 
|  | #include "gdbcmd.h" | 
|  | #include "elf-bfd.h" | 
|  | #include "gdb_bfd.h" | 
|  |  | 
|  | #define GOT_MODULE_OFFSET 4 | 
|  |  | 
|  | /* Flag which indicates whether internal debug messages should be printed.  */ | 
|  | static unsigned int solib_dsbt_debug = 0; | 
|  |  | 
|  | /* TIC6X pointers are four bytes wide.  */ | 
|  | enum { TIC6X_PTR_SIZE = 4 }; | 
|  |  | 
|  | /* Representation of loadmap and related structs for the TIC6X DSBT.  */ | 
|  |  | 
|  | /* External versions; the size and alignment of the fields should be | 
|  | the same as those on the target.  When loaded, the placement of | 
|  | the bits in each field will be the same as on the target.  */ | 
|  | typedef gdb_byte ext_Elf32_Half[2]; | 
|  | typedef gdb_byte ext_Elf32_Addr[4]; | 
|  | typedef gdb_byte ext_Elf32_Word[4]; | 
|  |  | 
|  | struct ext_elf32_dsbt_loadseg | 
|  | { | 
|  | /* Core address to which the segment is mapped.  */ | 
|  | ext_Elf32_Addr addr; | 
|  | /* VMA recorded in the program header.  */ | 
|  | ext_Elf32_Addr p_vaddr; | 
|  | /* Size of this segment in memory.  */ | 
|  | ext_Elf32_Word p_memsz; | 
|  | }; | 
|  |  | 
|  | struct ext_elf32_dsbt_loadmap { | 
|  | /* Protocol version number, must be zero.  */ | 
|  | ext_Elf32_Word version; | 
|  | /* A pointer to the DSBT table; the DSBT size and the index of this | 
|  | module.  */ | 
|  | ext_Elf32_Word dsbt_table_ptr; | 
|  | ext_Elf32_Word dsbt_size; | 
|  | ext_Elf32_Word dsbt_index; | 
|  | /* Number of segments in this map.  */ | 
|  | ext_Elf32_Word nsegs; | 
|  | /* The actual memory map.  */ | 
|  | struct ext_elf32_dsbt_loadseg segs[1 /* nsegs, actually */]; | 
|  | }; | 
|  |  | 
|  | /* Internal versions; the types are GDB types and the data in each | 
|  | of the fields is (or will be) decoded from the external struct | 
|  | for ease of consumption.  */ | 
|  | struct int_elf32_dsbt_loadseg | 
|  | { | 
|  | /* Core address to which the segment is mapped.  */ | 
|  | CORE_ADDR addr; | 
|  | /* VMA recorded in the program header.  */ | 
|  | CORE_ADDR p_vaddr; | 
|  | /* Size of this segment in memory.  */ | 
|  | long p_memsz; | 
|  | }; | 
|  |  | 
|  | struct int_elf32_dsbt_loadmap | 
|  | { | 
|  | /* Protocol version number, must be zero.  */ | 
|  | int version; | 
|  | CORE_ADDR dsbt_table_ptr; | 
|  | /* A pointer to the DSBT table; the DSBT size and the index of this | 
|  | module.  */ | 
|  | int dsbt_size, dsbt_index; | 
|  | /* Number of segments in this map.  */ | 
|  | int nsegs; | 
|  | /* The actual memory map.  */ | 
|  | struct int_elf32_dsbt_loadseg segs[1 /* nsegs, actually */]; | 
|  | }; | 
|  |  | 
|  | /* External link_map and elf32_dsbt_loadaddr struct definitions.  */ | 
|  |  | 
|  | typedef gdb_byte ext_ptr[4]; | 
|  |  | 
|  | struct ext_elf32_dsbt_loadaddr | 
|  | { | 
|  | ext_ptr map;			/* struct elf32_dsbt_loadmap *map; */ | 
|  | }; | 
|  |  | 
|  | struct dbst_ext_link_map | 
|  | { | 
|  | struct ext_elf32_dsbt_loadaddr l_addr; | 
|  |  | 
|  | /* Absolute file name object was found in.  */ | 
|  | ext_ptr l_name;		/* char *l_name; */ | 
|  |  | 
|  | /* Dynamic section of the shared object.  */ | 
|  | ext_ptr l_ld;			/* ElfW(Dyn) *l_ld; */ | 
|  |  | 
|  | /* Chain of loaded objects.  */ | 
|  | ext_ptr l_next, l_prev;	/* struct link_map *l_next, *l_prev; */ | 
|  | }; | 
|  |  | 
|  | /* Link map info to include in an allocated so_list entry */ | 
|  |  | 
|  | struct lm_info_dsbt : public lm_info_base | 
|  | { | 
|  | ~lm_info_dsbt () | 
|  | { | 
|  | xfree (this->map); | 
|  | } | 
|  |  | 
|  | /* The loadmap, digested into an easier to use form.  */ | 
|  | int_elf32_dsbt_loadmap *map = NULL; | 
|  | }; | 
|  |  | 
|  | /* Per pspace dsbt specific data.  */ | 
|  |  | 
|  | struct dsbt_info | 
|  | { | 
|  | /* The load map, got value, etc. are not available from the chain | 
|  | of loaded shared objects.  ``main_executable_lm_info'' provides | 
|  | a way to get at this information so that it doesn't need to be | 
|  | frequently recomputed.  Initialized by dsbt_relocate_main_executable.  */ | 
|  | struct lm_info_dsbt *main_executable_lm_info = nullptr; | 
|  |  | 
|  | /* Load maps for the main executable and the interpreter.  These are obtained | 
|  | from ptrace.  They are the starting point for getting into the program, | 
|  | and are required to find the solib list with the individual load maps for | 
|  | each module.  */ | 
|  | struct int_elf32_dsbt_loadmap *exec_loadmap = nullptr; | 
|  | struct int_elf32_dsbt_loadmap *interp_loadmap = nullptr; | 
|  |  | 
|  | /* Cached value for lm_base, below.  */ | 
|  | CORE_ADDR lm_base_cache = 0; | 
|  |  | 
|  | /* Link map address for main module.  */ | 
|  | CORE_ADDR main_lm_addr = 0; | 
|  |  | 
|  | CORE_ADDR interp_text_sect_low = 0; | 
|  | CORE_ADDR interp_text_sect_high = 0; | 
|  | CORE_ADDR interp_plt_sect_low = 0; | 
|  | CORE_ADDR interp_plt_sect_high = 0; | 
|  | }; | 
|  |  | 
|  | /* Per-program-space data key.  */ | 
|  | static const registry<program_space>::key<dsbt_info> solib_dsbt_pspace_data; | 
|  |  | 
|  | /* Get the current dsbt data.  If none is found yet, add it now.  This | 
|  | function always returns a valid object.  */ | 
|  |  | 
|  | static struct dsbt_info * | 
|  | get_dsbt_info (void) | 
|  | { | 
|  | struct dsbt_info *info; | 
|  |  | 
|  | info = solib_dsbt_pspace_data.get (current_program_space); | 
|  | if (info != NULL) | 
|  | return info; | 
|  |  | 
|  | return solib_dsbt_pspace_data.emplace (current_program_space); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | dsbt_print_loadmap (struct int_elf32_dsbt_loadmap *map) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (map == NULL) | 
|  | gdb_printf ("(null)\n"); | 
|  | else if (map->version != 0) | 
|  | gdb_printf (_("Unsupported map version: %d\n"), map->version); | 
|  | else | 
|  | { | 
|  | gdb_printf ("version %d\n", map->version); | 
|  |  | 
|  | for (i = 0; i < map->nsegs; i++) | 
|  | gdb_printf ("%s:%s -> %s:%s\n", | 
|  | print_core_address (target_gdbarch (), | 
|  | map->segs[i].p_vaddr), | 
|  | print_core_address (target_gdbarch (), | 
|  | map->segs[i].p_vaddr | 
|  | + map->segs[i].p_memsz), | 
|  | print_core_address (target_gdbarch (), map->segs[i].addr), | 
|  | print_core_address (target_gdbarch (), map->segs[i].addr | 
|  | + map->segs[i].p_memsz)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Decode int_elf32_dsbt_loadmap from BUF.  */ | 
|  |  | 
|  | static struct int_elf32_dsbt_loadmap * | 
|  | decode_loadmap (const gdb_byte *buf) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); | 
|  | const struct ext_elf32_dsbt_loadmap *ext_ldmbuf; | 
|  | struct int_elf32_dsbt_loadmap *int_ldmbuf; | 
|  |  | 
|  | int version, seg, nsegs; | 
|  | int int_ldmbuf_size; | 
|  |  | 
|  | ext_ldmbuf = (struct ext_elf32_dsbt_loadmap *) buf; | 
|  |  | 
|  | /* Extract the version.  */ | 
|  | version = extract_unsigned_integer (ext_ldmbuf->version, | 
|  | sizeof ext_ldmbuf->version, | 
|  | byte_order); | 
|  | if (version != 0) | 
|  | { | 
|  | /* We only handle version 0.  */ | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Extract the number of segments.  */ | 
|  | nsegs = extract_unsigned_integer (ext_ldmbuf->nsegs, | 
|  | sizeof ext_ldmbuf->nsegs, | 
|  | byte_order); | 
|  |  | 
|  | if (nsegs <= 0) | 
|  | return NULL; | 
|  |  | 
|  | /* Allocate space into which to put information extract from the | 
|  | external loadsegs.  I.e, allocate the internal loadsegs.  */ | 
|  | int_ldmbuf_size = (sizeof (struct int_elf32_dsbt_loadmap) | 
|  | + (nsegs - 1) * sizeof (struct int_elf32_dsbt_loadseg)); | 
|  | int_ldmbuf = (struct int_elf32_dsbt_loadmap *) xmalloc (int_ldmbuf_size); | 
|  |  | 
|  | /* Place extracted information in internal structs.  */ | 
|  | int_ldmbuf->version = version; | 
|  | int_ldmbuf->nsegs = nsegs; | 
|  | for (seg = 0; seg < nsegs; seg++) | 
|  | { | 
|  | int_ldmbuf->segs[seg].addr | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].addr, | 
|  | sizeof (ext_ldmbuf->segs[seg].addr), | 
|  | byte_order); | 
|  | int_ldmbuf->segs[seg].p_vaddr | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].p_vaddr, | 
|  | sizeof (ext_ldmbuf->segs[seg].p_vaddr), | 
|  | byte_order); | 
|  | int_ldmbuf->segs[seg].p_memsz | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].p_memsz, | 
|  | sizeof (ext_ldmbuf->segs[seg].p_memsz), | 
|  | byte_order); | 
|  | } | 
|  |  | 
|  | return int_ldmbuf; | 
|  | } | 
|  |  | 
|  |  | 
|  | static struct dsbt_info *get_dsbt_info (void); | 
|  |  | 
|  | /* Interrogate the Linux kernel to find out where the program was loaded. | 
|  | There are two load maps; one for the executable and one for the | 
|  | interpreter (only in the case of a dynamically linked executable).  */ | 
|  |  | 
|  | static void | 
|  | dsbt_get_initial_loadmaps (void) | 
|  | { | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  | gdb::optional<gdb::byte_vector> buf | 
|  | = target_read_alloc (current_inferior ()->top_target (), | 
|  | TARGET_OBJECT_FDPIC, "exec"); | 
|  |  | 
|  | if (!buf || buf->empty ()) | 
|  | { | 
|  | info->exec_loadmap = NULL; | 
|  | error (_("Error reading DSBT exec loadmap")); | 
|  | } | 
|  | info->exec_loadmap = decode_loadmap (buf->data ()); | 
|  | if (solib_dsbt_debug) | 
|  | dsbt_print_loadmap (info->exec_loadmap); | 
|  |  | 
|  | buf = target_read_alloc (current_inferior ()->top_target (), | 
|  | TARGET_OBJECT_FDPIC, "exec"); | 
|  | if (!buf || buf->empty ()) | 
|  | { | 
|  | info->interp_loadmap = NULL; | 
|  | error (_("Error reading DSBT interp loadmap")); | 
|  | } | 
|  | info->interp_loadmap = decode_loadmap (buf->data ()); | 
|  | if (solib_dsbt_debug) | 
|  | dsbt_print_loadmap (info->interp_loadmap); | 
|  | } | 
|  |  | 
|  | /* Given address LDMADDR, fetch and decode the loadmap at that address. | 
|  | Return NULL if there is a problem reading the target memory or if | 
|  | there doesn't appear to be a loadmap at the given address.  The | 
|  | allocated space (representing the loadmap) returned by this | 
|  | function may be freed via a single call to xfree.  */ | 
|  |  | 
|  | static struct int_elf32_dsbt_loadmap * | 
|  | fetch_loadmap (CORE_ADDR ldmaddr) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); | 
|  | struct ext_elf32_dsbt_loadmap ext_ldmbuf_partial; | 
|  | struct ext_elf32_dsbt_loadmap *ext_ldmbuf; | 
|  | struct int_elf32_dsbt_loadmap *int_ldmbuf; | 
|  | int ext_ldmbuf_size, int_ldmbuf_size; | 
|  | int version, seg, nsegs; | 
|  |  | 
|  | /* Fetch initial portion of the loadmap.  */ | 
|  | if (target_read_memory (ldmaddr, (gdb_byte *) &ext_ldmbuf_partial, | 
|  | sizeof ext_ldmbuf_partial)) | 
|  | { | 
|  | /* Problem reading the target's memory.  */ | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Extract the version.  */ | 
|  | version = extract_unsigned_integer (ext_ldmbuf_partial.version, | 
|  | sizeof ext_ldmbuf_partial.version, | 
|  | byte_order); | 
|  | if (version != 0) | 
|  | { | 
|  | /* We only handle version 0.  */ | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Extract the number of segments.  */ | 
|  | nsegs = extract_unsigned_integer (ext_ldmbuf_partial.nsegs, | 
|  | sizeof ext_ldmbuf_partial.nsegs, | 
|  | byte_order); | 
|  |  | 
|  | if (nsegs <= 0) | 
|  | return NULL; | 
|  |  | 
|  | /* Allocate space for the complete (external) loadmap.  */ | 
|  | ext_ldmbuf_size = sizeof (struct ext_elf32_dsbt_loadmap) | 
|  | + (nsegs - 1) * sizeof (struct ext_elf32_dsbt_loadseg); | 
|  | ext_ldmbuf = (struct ext_elf32_dsbt_loadmap *) xmalloc (ext_ldmbuf_size); | 
|  |  | 
|  | /* Copy over the portion of the loadmap that's already been read.  */ | 
|  | memcpy (ext_ldmbuf, &ext_ldmbuf_partial, sizeof ext_ldmbuf_partial); | 
|  |  | 
|  | /* Read the rest of the loadmap from the target.  */ | 
|  | if (target_read_memory (ldmaddr + sizeof ext_ldmbuf_partial, | 
|  | (gdb_byte *) ext_ldmbuf + sizeof ext_ldmbuf_partial, | 
|  | ext_ldmbuf_size - sizeof ext_ldmbuf_partial)) | 
|  | { | 
|  | /* Couldn't read rest of the loadmap.  */ | 
|  | xfree (ext_ldmbuf); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Allocate space into which to put information extract from the | 
|  | external loadsegs.  I.e, allocate the internal loadsegs.  */ | 
|  | int_ldmbuf_size = sizeof (struct int_elf32_dsbt_loadmap) | 
|  | + (nsegs - 1) * sizeof (struct int_elf32_dsbt_loadseg); | 
|  | int_ldmbuf = (struct int_elf32_dsbt_loadmap *) xmalloc (int_ldmbuf_size); | 
|  |  | 
|  | /* Place extracted information in internal structs.  */ | 
|  | int_ldmbuf->version = version; | 
|  | int_ldmbuf->nsegs = nsegs; | 
|  | for (seg = 0; seg < nsegs; seg++) | 
|  | { | 
|  | int_ldmbuf->segs[seg].addr | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].addr, | 
|  | sizeof (ext_ldmbuf->segs[seg].addr), | 
|  | byte_order); | 
|  | int_ldmbuf->segs[seg].p_vaddr | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].p_vaddr, | 
|  | sizeof (ext_ldmbuf->segs[seg].p_vaddr), | 
|  | byte_order); | 
|  | int_ldmbuf->segs[seg].p_memsz | 
|  | = extract_unsigned_integer (ext_ldmbuf->segs[seg].p_memsz, | 
|  | sizeof (ext_ldmbuf->segs[seg].p_memsz), | 
|  | byte_order); | 
|  | } | 
|  |  | 
|  | xfree (ext_ldmbuf); | 
|  | return int_ldmbuf; | 
|  | } | 
|  |  | 
|  | static void dsbt_relocate_main_executable (void); | 
|  | static int enable_break (void); | 
|  |  | 
|  | /* See solist.h. */ | 
|  |  | 
|  | static int | 
|  | open_symbol_file_object (int from_tty) | 
|  | { | 
|  | /* Unimplemented.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Given a loadmap and an address, return the displacement needed | 
|  | to relocate the address.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | displacement_from_map (struct int_elf32_dsbt_loadmap *map, | 
|  | CORE_ADDR addr) | 
|  | { | 
|  | int seg; | 
|  |  | 
|  | for (seg = 0; seg < map->nsegs; seg++) | 
|  | if (map->segs[seg].p_vaddr <= addr | 
|  | && addr < map->segs[seg].p_vaddr + map->segs[seg].p_memsz) | 
|  | return map->segs[seg].addr - map->segs[seg].p_vaddr; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Return the address from which the link map chain may be found.  On | 
|  | DSBT, a pointer to the start of the link map will be located at the | 
|  | word found at base of GOT + GOT_MODULE_OFFSET. | 
|  |  | 
|  | The base of GOT may be found in a number of ways.  Assuming that the | 
|  | main executable has already been relocated, | 
|  | 1 The easiest way to find this value is to look up the address of | 
|  | _GLOBAL_OFFSET_TABLE_. | 
|  | 2 The other way is to look for tag DT_PLTGOT, which contains the virtual | 
|  | address of Global Offset Table.  .*/ | 
|  |  | 
|  | static CORE_ADDR | 
|  | lm_base (void) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); | 
|  | struct bound_minimal_symbol got_sym; | 
|  | CORE_ADDR addr; | 
|  | gdb_byte buf[TIC6X_PTR_SIZE]; | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  |  | 
|  | /* One of our assumptions is that the main executable has been relocated. | 
|  | Bail out if this has not happened.  (Note that post_create_inferior | 
|  | in infcmd.c will call solib_add prior to solib_create_inferior_hook. | 
|  | If we allow this to happen, lm_base_cache will be initialized with | 
|  | a bogus value.  */ | 
|  | if (info->main_executable_lm_info == 0) | 
|  | return 0; | 
|  |  | 
|  | /* If we already have a cached value, return it.  */ | 
|  | if (info->lm_base_cache) | 
|  | return info->lm_base_cache; | 
|  |  | 
|  | got_sym = lookup_minimal_symbol ("_GLOBAL_OFFSET_TABLE_", NULL, | 
|  | current_program_space->symfile_object_file); | 
|  |  | 
|  | if (got_sym.minsym != 0) | 
|  | { | 
|  | addr = got_sym.value_address (); | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "lm_base: get addr %x by _GLOBAL_OFFSET_TABLE_.\n", | 
|  | (unsigned int) addr); | 
|  | } | 
|  | else if (gdb_bfd_scan_elf_dyntag (DT_PLTGOT, | 
|  | current_program_space->exec_bfd (), | 
|  | &addr, NULL)) | 
|  | { | 
|  | struct int_elf32_dsbt_loadmap *ldm; | 
|  |  | 
|  | dsbt_get_initial_loadmaps (); | 
|  | ldm = info->exec_loadmap; | 
|  | addr += displacement_from_map (ldm, addr); | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "lm_base: get addr %x by DT_PLTGOT.\n", | 
|  | (unsigned int) addr); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "lm_base: _GLOBAL_OFFSET_TABLE_ not found.\n"); | 
|  | return 0; | 
|  | } | 
|  | addr += GOT_MODULE_OFFSET; | 
|  |  | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "lm_base: _GLOBAL_OFFSET_TABLE_ + %d = %s\n", | 
|  | GOT_MODULE_OFFSET, hex_string_custom (addr, 8)); | 
|  |  | 
|  | if (target_read_memory (addr, buf, sizeof buf) != 0) | 
|  | return 0; | 
|  | info->lm_base_cache = extract_unsigned_integer (buf, sizeof buf, byte_order); | 
|  |  | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "lm_base: lm_base_cache = %s\n", | 
|  | hex_string_custom (info->lm_base_cache, 8)); | 
|  |  | 
|  | return info->lm_base_cache; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Build a list of `struct so_list' objects describing the shared | 
|  | objects currently loaded in the inferior.  This list does not | 
|  | include an entry for the main executable file. | 
|  |  | 
|  | Note that we only gather information directly available from the | 
|  | inferior --- we don't examine any of the shared library files | 
|  | themselves.  The declaration of `struct so_list' says which fields | 
|  | we provide values for.  */ | 
|  |  | 
|  | static struct so_list * | 
|  | dsbt_current_sos (void) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); | 
|  | CORE_ADDR lm_addr; | 
|  | struct so_list *sos_head = NULL; | 
|  | struct so_list **sos_next_ptr = &sos_head; | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  |  | 
|  | /* Make sure that the main executable has been relocated.  This is | 
|  | required in order to find the address of the global offset table, | 
|  | which in turn is used to find the link map info.  (See lm_base | 
|  | for details.) | 
|  |  | 
|  | Note that the relocation of the main executable is also performed | 
|  | by solib_create_inferior_hook, however, in the case of core | 
|  | files, this hook is called too late in order to be of benefit to | 
|  | solib_add.  solib_add eventually calls this function, | 
|  | dsbt_current_sos, and also precedes the call to | 
|  | solib_create_inferior_hook.   (See post_create_inferior in | 
|  | infcmd.c.)  */ | 
|  | if (info->main_executable_lm_info == 0 && core_bfd != NULL) | 
|  | dsbt_relocate_main_executable (); | 
|  |  | 
|  | /* Locate the address of the first link map struct.  */ | 
|  | lm_addr = lm_base (); | 
|  |  | 
|  | /* We have at least one link map entry.  Fetch the lot of them, | 
|  | building the solist chain.  */ | 
|  | while (lm_addr) | 
|  | { | 
|  | struct dbst_ext_link_map lm_buf; | 
|  | ext_Elf32_Word indexword; | 
|  | CORE_ADDR map_addr; | 
|  | int dsbt_index; | 
|  | int ret; | 
|  |  | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "current_sos: reading link_map entry at %s\n", | 
|  | hex_string_custom (lm_addr, 8)); | 
|  |  | 
|  | ret = target_read_memory (lm_addr, (gdb_byte *) &lm_buf, sizeof (lm_buf)); | 
|  | if (ret) | 
|  | { | 
|  | warning (_("dsbt_current_sos: Unable to read link map entry." | 
|  | "  Shared object chain may be incomplete.")); | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Fetch the load map address.  */ | 
|  | map_addr = extract_unsigned_integer (lm_buf.l_addr.map, | 
|  | sizeof lm_buf.l_addr.map, | 
|  | byte_order); | 
|  |  | 
|  | ret = target_read_memory (map_addr + 12, (gdb_byte *) &indexword, | 
|  | sizeof indexword); | 
|  | if (ret) | 
|  | { | 
|  | warning (_("dsbt_current_sos: Unable to read dsbt index." | 
|  | "  Shared object chain may be incomplete.")); | 
|  | break; | 
|  | } | 
|  | dsbt_index = extract_unsigned_integer (indexword, sizeof indexword, | 
|  | byte_order); | 
|  |  | 
|  | /* If the DSBT index is zero, then we're looking at the entry | 
|  | for the main executable.  By convention, we don't include | 
|  | this in the list of shared objects.  */ | 
|  | if (dsbt_index != 0) | 
|  | { | 
|  | struct int_elf32_dsbt_loadmap *loadmap; | 
|  | struct so_list *sop; | 
|  | CORE_ADDR addr; | 
|  |  | 
|  | loadmap = fetch_loadmap (map_addr); | 
|  | if (loadmap == NULL) | 
|  | { | 
|  | warning (_("dsbt_current_sos: Unable to fetch load map." | 
|  | "  Shared object chain may be incomplete.")); | 
|  | break; | 
|  | } | 
|  |  | 
|  | sop = XCNEW (struct so_list); | 
|  | lm_info_dsbt *li = new lm_info_dsbt; | 
|  | sop->lm_info = li; | 
|  | li->map = loadmap; | 
|  | /* Fetch the name.  */ | 
|  | addr = extract_unsigned_integer (lm_buf.l_name, | 
|  | sizeof (lm_buf.l_name), | 
|  | byte_order); | 
|  | gdb::unique_xmalloc_ptr<char> name_buf | 
|  | = target_read_string (addr, SO_NAME_MAX_PATH_SIZE - 1); | 
|  |  | 
|  | if (name_buf == nullptr) | 
|  | warning (_("Can't read pathname for link map entry.")); | 
|  | else | 
|  | { | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, "current_sos: name = %s\n", | 
|  | name_buf.get ()); | 
|  |  | 
|  | strncpy (sop->so_name, name_buf.get (), SO_NAME_MAX_PATH_SIZE - 1); | 
|  | sop->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; | 
|  | strcpy (sop->so_original_name, sop->so_name); | 
|  | } | 
|  |  | 
|  | *sos_next_ptr = sop; | 
|  | sos_next_ptr = &sop->next; | 
|  | } | 
|  | else | 
|  | { | 
|  | info->main_lm_addr = lm_addr; | 
|  | } | 
|  |  | 
|  | lm_addr = extract_unsigned_integer (lm_buf.l_next, | 
|  | sizeof (lm_buf.l_next), byte_order); | 
|  | } | 
|  |  | 
|  | return sos_head; | 
|  | } | 
|  |  | 
|  | /* Return 1 if PC lies in the dynamic symbol resolution code of the | 
|  | run time loader.  */ | 
|  |  | 
|  | static int | 
|  | dsbt_in_dynsym_resolve_code (CORE_ADDR pc) | 
|  | { | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  |  | 
|  | return ((pc >= info->interp_text_sect_low && pc < info->interp_text_sect_high) | 
|  | || (pc >= info->interp_plt_sect_low && pc < info->interp_plt_sect_high) | 
|  | || in_plt_section (pc)); | 
|  | } | 
|  |  | 
|  | /* Print a warning about being unable to set the dynamic linker | 
|  | breakpoint.  */ | 
|  |  | 
|  | static void | 
|  | enable_break_failure_warning (void) | 
|  | { | 
|  | warning (_("Unable to find dynamic linker breakpoint function.\n" | 
|  | "GDB will be unable to debug shared library initializers\n" | 
|  | "and track explicitly loaded dynamic code.")); | 
|  | } | 
|  |  | 
|  | /* Helper function for gdb_bfd_lookup_symbol.  */ | 
|  |  | 
|  | static int | 
|  | cmp_name (const asymbol *sym, const void *data) | 
|  | { | 
|  | return (strcmp (sym->name, (const char *) data) == 0); | 
|  | } | 
|  |  | 
|  | /* The dynamic linkers has, as part of its debugger interface, support | 
|  | for arranging for the inferior to hit a breakpoint after mapping in | 
|  | the shared libraries.  This function enables that breakpoint. | 
|  |  | 
|  | On the TIC6X, using the shared library (DSBT), GDB can try to place | 
|  | a breakpoint on '_dl_debug_state' to monitor the shared library | 
|  | event.  */ | 
|  |  | 
|  | static int | 
|  | enable_break (void) | 
|  | { | 
|  | asection *interp_sect; | 
|  | struct dsbt_info *info; | 
|  |  | 
|  | if (current_program_space->exec_bfd () == NULL) | 
|  | return 0; | 
|  |  | 
|  | if (!target_has_execution ()) | 
|  | return 0; | 
|  |  | 
|  | info = get_dsbt_info (); | 
|  |  | 
|  | info->interp_text_sect_low = 0; | 
|  | info->interp_text_sect_high = 0; | 
|  | info->interp_plt_sect_low = 0; | 
|  | info->interp_plt_sect_high = 0; | 
|  |  | 
|  | /* Find the .interp section; if not found, warn the user and drop | 
|  | into the old breakpoint at symbol code.  */ | 
|  | interp_sect = bfd_get_section_by_name (current_program_space->exec_bfd (), | 
|  | ".interp"); | 
|  | if (interp_sect) | 
|  | { | 
|  | unsigned int interp_sect_size; | 
|  | char *buf; | 
|  | CORE_ADDR addr; | 
|  | struct int_elf32_dsbt_loadmap *ldm; | 
|  | int ret; | 
|  |  | 
|  | /* Read the contents of the .interp section into a local buffer; | 
|  | the contents specify the dynamic linker this program uses.  */ | 
|  | interp_sect_size = bfd_section_size (interp_sect); | 
|  | buf = (char *) alloca (interp_sect_size); | 
|  | bfd_get_section_contents (current_program_space->exec_bfd (), | 
|  | interp_sect, buf, 0, interp_sect_size); | 
|  |  | 
|  | /* Now we need to figure out where the dynamic linker was | 
|  | loaded so that we can load its symbols and place a breakpoint | 
|  | in the dynamic linker itself.  */ | 
|  |  | 
|  | gdb_bfd_ref_ptr tmp_bfd; | 
|  | try | 
|  | { | 
|  | tmp_bfd = solib_bfd_open (buf); | 
|  | } | 
|  | catch (const gdb_exception &ex) | 
|  | { | 
|  | } | 
|  |  | 
|  | if (tmp_bfd == NULL) | 
|  | { | 
|  | enable_break_failure_warning (); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | dsbt_get_initial_loadmaps (); | 
|  | ldm = info->interp_loadmap; | 
|  |  | 
|  | /* Record the relocated start and end address of the dynamic linker | 
|  | text and plt section for dsbt_in_dynsym_resolve_code.  */ | 
|  | interp_sect = bfd_get_section_by_name (tmp_bfd.get (), ".text"); | 
|  | if (interp_sect) | 
|  | { | 
|  | info->interp_text_sect_low = bfd_section_vma (interp_sect); | 
|  | info->interp_text_sect_low | 
|  | += displacement_from_map (ldm, info->interp_text_sect_low); | 
|  | info->interp_text_sect_high | 
|  | = info->interp_text_sect_low + bfd_section_size (interp_sect); | 
|  | } | 
|  | interp_sect = bfd_get_section_by_name (tmp_bfd.get (), ".plt"); | 
|  | if (interp_sect) | 
|  | { | 
|  | info->interp_plt_sect_low = bfd_section_vma (interp_sect); | 
|  | info->interp_plt_sect_low | 
|  | += displacement_from_map (ldm, info->interp_plt_sect_low); | 
|  | info->interp_plt_sect_high | 
|  | = info->interp_plt_sect_low + bfd_section_size (interp_sect); | 
|  | } | 
|  |  | 
|  | addr = gdb_bfd_lookup_symbol (tmp_bfd.get (), cmp_name, | 
|  | "_dl_debug_state"); | 
|  | if (addr != 0) | 
|  | { | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "enable_break: _dl_debug_state (prior to relocation) = %s\n", | 
|  | hex_string_custom (addr, 8)); | 
|  | addr += displacement_from_map (ldm, addr); | 
|  |  | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "enable_break: _dl_debug_state (after relocation) = %s\n", | 
|  | hex_string_custom (addr, 8)); | 
|  |  | 
|  | /* Now (finally!) create the solib breakpoint.  */ | 
|  | create_solib_event_breakpoint (target_gdbarch (), addr); | 
|  |  | 
|  | ret = 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (solib_dsbt_debug) | 
|  | gdb_printf (gdb_stdlog, | 
|  | "enable_break: _dl_debug_state is not found\n"); | 
|  | ret = 0; | 
|  | } | 
|  |  | 
|  | /* We're done with the loadmap.  */ | 
|  | xfree (ldm); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Tell the user we couldn't set a dynamic linker breakpoint.  */ | 
|  | enable_break_failure_warning (); | 
|  |  | 
|  | /* Failure return.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dsbt_relocate_main_executable (void) | 
|  | { | 
|  | struct int_elf32_dsbt_loadmap *ldm; | 
|  | int changed; | 
|  | struct obj_section *osect; | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  |  | 
|  | dsbt_get_initial_loadmaps (); | 
|  | ldm = info->exec_loadmap; | 
|  |  | 
|  | delete info->main_executable_lm_info; | 
|  | info->main_executable_lm_info = new lm_info_dsbt; | 
|  | info->main_executable_lm_info->map = ldm; | 
|  |  | 
|  | objfile *objf = current_program_space->symfile_object_file; | 
|  | section_offsets new_offsets (objf->section_offsets.size ()); | 
|  | changed = 0; | 
|  |  | 
|  | ALL_OBJFILE_OSECTIONS (objf, osect) | 
|  | { | 
|  | CORE_ADDR orig_addr, addr, offset; | 
|  | int osect_idx; | 
|  | int seg; | 
|  |  | 
|  | osect_idx = osect - objf->sections; | 
|  |  | 
|  | /* Current address of section.  */ | 
|  | addr = osect->addr (); | 
|  | /* Offset from where this section started.  */ | 
|  | offset = objf->section_offsets[osect_idx]; | 
|  | /* Original address prior to any past relocations.  */ | 
|  | orig_addr = addr - offset; | 
|  |  | 
|  | for (seg = 0; seg < ldm->nsegs; seg++) | 
|  | { | 
|  | if (ldm->segs[seg].p_vaddr <= orig_addr | 
|  | && orig_addr < ldm->segs[seg].p_vaddr + ldm->segs[seg].p_memsz) | 
|  | { | 
|  | new_offsets[osect_idx] | 
|  | = ldm->segs[seg].addr - ldm->segs[seg].p_vaddr; | 
|  |  | 
|  | if (new_offsets[osect_idx] != offset) | 
|  | changed = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (changed) | 
|  | objfile_relocate (objf, new_offsets); | 
|  |  | 
|  | /* Now that OBJF has been relocated, we can compute the GOT value | 
|  | and stash it away.  */ | 
|  | } | 
|  |  | 
|  | /* When gdb starts up the inferior, it nurses it along (through the | 
|  | shell) until it is ready to execute it's first instruction.  At this | 
|  | point, this function gets called via solib_create_inferior_hook. | 
|  |  | 
|  | For the DSBT shared library, the main executable needs to be relocated. | 
|  | The shared library breakpoints also need to be enabled.  */ | 
|  |  | 
|  | static void | 
|  | dsbt_solib_create_inferior_hook (int from_tty) | 
|  | { | 
|  | /* Relocate main executable.  */ | 
|  | dsbt_relocate_main_executable (); | 
|  |  | 
|  | /* Enable shared library breakpoints.  */ | 
|  | if (!enable_break ()) | 
|  | { | 
|  | warning (_("shared library handler failed to enable breakpoint")); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dsbt_clear_solib (void) | 
|  | { | 
|  | struct dsbt_info *info = get_dsbt_info (); | 
|  |  | 
|  | info->lm_base_cache = 0; | 
|  | info->main_lm_addr = 0; | 
|  |  | 
|  | delete info->main_executable_lm_info; | 
|  | info->main_executable_lm_info = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dsbt_free_so (struct so_list *so) | 
|  | { | 
|  | lm_info_dsbt *li = (lm_info_dsbt *) so->lm_info; | 
|  |  | 
|  | delete li; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dsbt_relocate_section_addresses (struct so_list *so, | 
|  | struct target_section *sec) | 
|  | { | 
|  | int seg; | 
|  | lm_info_dsbt *li = (lm_info_dsbt *) so->lm_info; | 
|  | int_elf32_dsbt_loadmap *map = li->map; | 
|  |  | 
|  | for (seg = 0; seg < map->nsegs; seg++) | 
|  | { | 
|  | if (map->segs[seg].p_vaddr <= sec->addr | 
|  | && sec->addr < map->segs[seg].p_vaddr + map->segs[seg].p_memsz) | 
|  | { | 
|  | CORE_ADDR displ = map->segs[seg].addr - map->segs[seg].p_vaddr; | 
|  |  | 
|  | sec->addr += displ; | 
|  | sec->endaddr += displ; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | static void | 
|  | show_dsbt_debug (struct ui_file *file, int from_tty, | 
|  | struct cmd_list_element *c, const char *value) | 
|  | { | 
|  | gdb_printf (file, _("solib-dsbt debugging is %s.\n"), value); | 
|  | } | 
|  |  | 
|  | struct target_so_ops dsbt_so_ops; | 
|  |  | 
|  | void _initialize_dsbt_solib (); | 
|  | void | 
|  | _initialize_dsbt_solib () | 
|  | { | 
|  | dsbt_so_ops.relocate_section_addresses = dsbt_relocate_section_addresses; | 
|  | dsbt_so_ops.free_so = dsbt_free_so; | 
|  | dsbt_so_ops.clear_solib = dsbt_clear_solib; | 
|  | dsbt_so_ops.solib_create_inferior_hook = dsbt_solib_create_inferior_hook; | 
|  | dsbt_so_ops.current_sos = dsbt_current_sos; | 
|  | dsbt_so_ops.open_symbol_file_object = open_symbol_file_object; | 
|  | dsbt_so_ops.in_dynsym_resolve_code = dsbt_in_dynsym_resolve_code; | 
|  | dsbt_so_ops.bfd_open = solib_bfd_open; | 
|  |  | 
|  | /* Debug this file's internals.  */ | 
|  | add_setshow_zuinteger_cmd ("solib-dsbt", class_maintenance, | 
|  | &solib_dsbt_debug, _("\ | 
|  | Set internal debugging of shared library code for DSBT ELF."), _("\ | 
|  | Show internal debugging of shared library code for DSBT ELF."), _("\ | 
|  | When non-zero, DSBT solib specific internal debugging is enabled."), | 
|  | NULL, | 
|  | show_dsbt_debug, | 
|  | &setdebuglist, &showdebuglist); | 
|  | } |