| /* Handle FR-V (FDPIC) shared libraries for GDB, the GNU Debugger. |
| Copyright (C) 2004-2021 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 "frv-tdep.h" |
| #include "objfiles.h" |
| #include "symtab.h" |
| #include "language.h" |
| #include "command.h" |
| #include "gdbcmd.h" |
| #include "elf/frv.h" |
| #include "gdb_bfd.h" |
| |
| /* Flag which indicates whether internal debug messages should be printed. */ |
| static unsigned int solib_frv_debug; |
| |
| /* FR-V pointers are four bytes wide. */ |
| enum { FRV_PTR_SIZE = 4 }; |
| |
| /* Representation of loadmap and related structs for the FR-V FDPIC ABI. */ |
| |
| /* 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_fdpic_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_fdpic_loadmap { |
| /* Protocol version number, must be zero. */ |
| ext_Elf32_Half version; |
| /* Number of segments in this map. */ |
| ext_Elf32_Half nsegs; |
| /* The actual memory map. */ |
| struct ext_elf32_fdpic_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_fdpic_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_fdpic_loadmap { |
| /* Protocol version number, must be zero. */ |
| int version; |
| /* Number of segments in this map. */ |
| int nsegs; |
| /* The actual memory map. */ |
| struct int_elf32_fdpic_loadseg segs[1 /* nsegs, actually */]; |
| }; |
| |
| /* 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_fdpic_loadmap * |
| fetch_loadmap (CORE_ADDR ldmaddr) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); |
| struct ext_elf32_fdpic_loadmap ext_ldmbuf_partial; |
| struct ext_elf32_fdpic_loadmap *ext_ldmbuf; |
| struct int_elf32_fdpic_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_fdpic_loadmap) |
| + (nsegs - 1) * sizeof (struct ext_elf32_fdpic_loadseg); |
| ext_ldmbuf = (struct ext_elf32_fdpic_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_fdpic_loadmap) |
| + (nsegs - 1) * sizeof (struct int_elf32_fdpic_loadseg); |
| int_ldmbuf = (struct int_elf32_fdpic_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; |
| } |
| |
| /* External link_map and elf32_fdpic_loadaddr struct definitions. */ |
| |
| typedef gdb_byte ext_ptr[4]; |
| |
| struct ext_elf32_fdpic_loadaddr |
| { |
| ext_ptr map; /* struct elf32_fdpic_loadmap *map; */ |
| ext_ptr got_value; /* void *got_value; */ |
| }; |
| |
| struct ext_link_map |
| { |
| struct ext_elf32_fdpic_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_frv : public lm_info_base |
| { |
| ~lm_info_frv () |
| { |
| xfree (this->map); |
| xfree (this->dyn_syms); |
| xfree (this->dyn_relocs); |
| } |
| |
| /* The loadmap, digested into an easier to use form. */ |
| int_elf32_fdpic_loadmap *map = NULL; |
| /* The GOT address for this link map entry. */ |
| CORE_ADDR got_value = 0; |
| /* The link map address, needed for frv_fetch_objfile_link_map(). */ |
| CORE_ADDR lm_addr = 0; |
| |
| /* Cached dynamic symbol table and dynamic relocs initialized and |
| used only by find_canonical_descriptor_in_load_object(). |
| |
| Note: kevinb/2004-02-26: It appears that calls to |
| bfd_canonicalize_dynamic_reloc() will use the same symbols as |
| those supplied to the first call to this function. Therefore, |
| it's important to NOT free the asymbol ** data structure |
| supplied to the first call. Thus the caching of the dynamic |
| symbols (dyn_syms) is critical for correct operation. The |
| caching of the dynamic relocations could be dispensed with. */ |
| asymbol **dyn_syms = NULL; |
| arelent **dyn_relocs = NULL; |
| int dyn_reloc_count = 0; /* Number of dynamic relocs. */ |
| }; |
| |
| /* 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 frv_relocate_main_executable(). */ |
| static lm_info_frv *main_executable_lm_info; |
| |
| static void frv_relocate_main_executable (void); |
| static CORE_ADDR main_got (void); |
| static int enable_break2 (void); |
| |
| /* Implement the "open_symbol_file_object" target_so_ops method. */ |
| |
| static int |
| open_symbol_file_object (int from_tty) |
| { |
| /* Unimplemented. */ |
| return 0; |
| } |
| |
| /* Cached value for lm_base(), below. */ |
| static CORE_ADDR lm_base_cache = 0; |
| |
| /* Link map address for main module. */ |
| static CORE_ADDR main_lm_addr = 0; |
| |
| /* Return the address from which the link map chain may be found. On |
| the FR-V, this may be found in a number of ways. Assuming that the |
| main executable has already been relocated, the easiest way to find |
| this value is to look up the address of _GLOBAL_OFFSET_TABLE_. A |
| pointer to the start of the link map will be located at the word found |
| at _GLOBAL_OFFSET_TABLE_ + 8. (This is part of the dynamic linker |
| reserve area mandated by the ABI.) */ |
| |
| 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[FRV_PTR_SIZE]; |
| |
| /* 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 (main_executable_lm_info == 0) |
| return 0; |
| |
| /* If we already have a cached value, return it. */ |
| if (lm_base_cache) |
| return lm_base_cache; |
| |
| got_sym = lookup_minimal_symbol ("_GLOBAL_OFFSET_TABLE_", NULL, |
| current_program_space->symfile_object_file); |
| if (got_sym.minsym == 0) |
| { |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "lm_base: _GLOBAL_OFFSET_TABLE_ not found.\n"); |
| return 0; |
| } |
| |
| addr = BMSYMBOL_VALUE_ADDRESS (got_sym) + 8; |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "lm_base: _GLOBAL_OFFSET_TABLE_ + 8 = %s\n", |
| hex_string_custom (addr, 8)); |
| |
| if (target_read_memory (addr, buf, sizeof buf) != 0) |
| return 0; |
| lm_base_cache = extract_unsigned_integer (buf, sizeof buf, byte_order); |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "lm_base: lm_base_cache = %s\n", |
| hex_string_custom (lm_base_cache, 8)); |
| |
| return lm_base_cache; |
| } |
| |
| |
| /* Implement the "current_sos" target_so_ops method. */ |
| |
| static struct so_list * |
| frv_current_sos (void) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); |
| CORE_ADDR lm_addr, mgot; |
| struct so_list *sos_head = NULL; |
| struct so_list **sos_next_ptr = &sos_head; |
| |
| /* 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 this function, |
| frv_current_sos, and also precedes the call to |
| solib_create_inferior_hook(). (See post_create_inferior() in |
| infcmd.c.) */ |
| if (main_executable_lm_info == 0 && core_bfd != NULL) |
| frv_relocate_main_executable (); |
| |
| /* Fetch the GOT corresponding to the main executable. */ |
| mgot = main_got (); |
| |
| /* 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 ext_link_map lm_buf; |
| CORE_ADDR got_addr; |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "current_sos: reading link_map entry at %s\n", |
| hex_string_custom (lm_addr, 8)); |
| |
| if (target_read_memory (lm_addr, (gdb_byte *) &lm_buf, |
| sizeof (lm_buf)) != 0) |
| { |
| warning (_("frv_current_sos: Unable to read link map entry. " |
| "Shared object chain may be incomplete.")); |
| break; |
| } |
| |
| got_addr |
| = extract_unsigned_integer (lm_buf.l_addr.got_value, |
| sizeof (lm_buf.l_addr.got_value), |
| byte_order); |
| /* If the got_addr is the same as mgotr, 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 (got_addr != mgot) |
| { |
| struct int_elf32_fdpic_loadmap *loadmap; |
| struct so_list *sop; |
| CORE_ADDR addr; |
| |
| /* Fetch the load map address. */ |
| addr = extract_unsigned_integer (lm_buf.l_addr.map, |
| sizeof lm_buf.l_addr.map, |
| byte_order); |
| loadmap = fetch_loadmap (addr); |
| if (loadmap == NULL) |
| { |
| warning (_("frv_current_sos: Unable to fetch load map. " |
| "Shared object chain may be incomplete.")); |
| break; |
| } |
| |
| sop = XCNEW (struct so_list); |
| lm_info_frv *li = new lm_info_frv; |
| sop->lm_info = li; |
| li->map = loadmap; |
| li->got_value = got_addr; |
| li->lm_addr = lm_addr; |
| /* 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 (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, "current_sos: name = %s\n", |
| name_buf.get ()); |
| |
| if (name_buf == nullptr) |
| warning (_("Can't read pathname for link map entry.")); |
| else |
| { |
| 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 |
| { |
| main_lm_addr = lm_addr; |
| } |
| |
| lm_addr = extract_unsigned_integer (lm_buf.l_next, |
| sizeof (lm_buf.l_next), byte_order); |
| } |
| |
| enable_break2 (); |
| |
| return sos_head; |
| } |
| |
| |
| /* Return 1 if PC lies in the dynamic symbol resolution code of the |
| run time loader. */ |
| |
| static CORE_ADDR interp_text_sect_low; |
| static CORE_ADDR interp_text_sect_high; |
| static CORE_ADDR interp_plt_sect_low; |
| static CORE_ADDR interp_plt_sect_high; |
| |
| static int |
| frv_in_dynsym_resolve_code (CORE_ADDR pc) |
| { |
| return ((pc >= interp_text_sect_low && pc < interp_text_sect_high) |
| || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high) |
| || in_plt_section (pc)); |
| } |
| |
| /* Given a loadmap and an address, return the displacement needed |
| to relocate the address. */ |
| |
| static CORE_ADDR |
| displacement_from_map (struct int_elf32_fdpic_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; |
| } |
| |
| /* 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); |
| } |
| |
| /* Arrange for dynamic linker to hit breakpoint. |
| |
| 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 FR-V, using the shared library (FDPIC) ABI, the symbol |
| _dl_debug_addr points to the r_debug struct which contains |
| a field called r_brk. r_brk is the address of the function |
| descriptor upon which a breakpoint must be placed. Being a |
| function descriptor, we must extract the entry point in order |
| to set the breakpoint. |
| |
| Our strategy will be to get the .interp section from the |
| executable. This section will provide us with the name of the |
| interpreter. We'll open the interpreter and then look up |
| the address of _dl_debug_addr. We then relocate this address |
| using the interpreter's loadmap. Once the relocated address |
| is known, we fetch the value (address) corresponding to r_brk |
| and then use that value to fetch the entry point of the function |
| we're interested in. */ |
| |
| static int enable_break2_done = 0; |
| |
| static int |
| enable_break2 (void) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); |
| asection *interp_sect; |
| |
| if (enable_break2_done) |
| return 1; |
| |
| interp_text_sect_low = interp_text_sect_high = 0; |
| interp_plt_sect_low = 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; |
| int status; |
| CORE_ADDR addr, interp_loadmap_addr; |
| gdb_byte addr_buf[FRV_PTR_SIZE]; |
| struct int_elf32_fdpic_loadmap *ldm; |
| |
| /* 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. |
| |
| This address is stored on the stack. However, I've been unable |
| to find any magic formula to find it for Solaris (appears to |
| be trivial on GNU/Linux). Therefore, we have to try an alternate |
| mechanism to find the dynamic linker's base address. */ |
| |
| 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; |
| } |
| |
| status = frv_fdpic_loadmap_addresses (target_gdbarch (), |
| &interp_loadmap_addr, 0); |
| if (status < 0) |
| { |
| warning (_("Unable to determine dynamic linker loadmap address.")); |
| enable_break_failure_warning (); |
| return 0; |
| } |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: interp_loadmap_addr = %s\n", |
| hex_string_custom (interp_loadmap_addr, 8)); |
| |
| ldm = fetch_loadmap (interp_loadmap_addr); |
| if (ldm == NULL) |
| { |
| warning (_("Unable to load dynamic linker loadmap at address %s."), |
| hex_string_custom (interp_loadmap_addr, 8)); |
| enable_break_failure_warning (); |
| return 0; |
| } |
| |
| /* Record the relocated start and end address of the dynamic linker |
| text and plt section for svr4_in_dynsym_resolve_code. */ |
| interp_sect = bfd_get_section_by_name (tmp_bfd.get (), ".text"); |
| if (interp_sect) |
| { |
| interp_text_sect_low = bfd_section_vma (interp_sect); |
| interp_text_sect_low |
| += displacement_from_map (ldm, interp_text_sect_low); |
| interp_text_sect_high |
| = interp_text_sect_low + bfd_section_size (interp_sect); |
| } |
| interp_sect = bfd_get_section_by_name (tmp_bfd.get (), ".plt"); |
| if (interp_sect) |
| { |
| interp_plt_sect_low = bfd_section_vma (interp_sect); |
| interp_plt_sect_low |
| += displacement_from_map (ldm, interp_plt_sect_low); |
| interp_plt_sect_high = |
| interp_plt_sect_low + bfd_section_size (interp_sect); |
| } |
| |
| addr = gdb_bfd_lookup_symbol (tmp_bfd.get (), cmp_name, "_dl_debug_addr"); |
| |
| if (addr == 0) |
| { |
| warning (_("Could not find symbol _dl_debug_addr " |
| "in dynamic linker")); |
| enable_break_failure_warning (); |
| return 0; |
| } |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: _dl_debug_addr " |
| "(prior to relocation) = %s\n", |
| hex_string_custom (addr, 8)); |
| |
| addr += displacement_from_map (ldm, addr); |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: _dl_debug_addr " |
| "(after relocation) = %s\n", |
| hex_string_custom (addr, 8)); |
| |
| /* Fetch the address of the r_debug struct. */ |
| if (target_read_memory (addr, addr_buf, sizeof addr_buf) != 0) |
| { |
| warning (_("Unable to fetch contents of _dl_debug_addr " |
| "(at address %s) from dynamic linker"), |
| hex_string_custom (addr, 8)); |
| } |
| addr = extract_unsigned_integer (addr_buf, sizeof addr_buf, byte_order); |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: _dl_debug_addr[0..3] = %s\n", |
| hex_string_custom (addr, 8)); |
| |
| /* If it's zero, then the ldso hasn't initialized yet, and so |
| there are no shared libs yet loaded. */ |
| if (addr == 0) |
| { |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: ldso not yet initialized\n"); |
| /* Do not warn, but mark to run again. */ |
| return 0; |
| } |
| |
| /* Fetch the r_brk field. It's 8 bytes from the start of |
| _dl_debug_addr. */ |
| if (target_read_memory (addr + 8, addr_buf, sizeof addr_buf) != 0) |
| { |
| warning (_("Unable to fetch _dl_debug_addr->r_brk " |
| "(at address %s) from dynamic linker"), |
| hex_string_custom (addr + 8, 8)); |
| enable_break_failure_warning (); |
| return 0; |
| } |
| addr = extract_unsigned_integer (addr_buf, sizeof addr_buf, byte_order); |
| |
| /* Now fetch the function entry point. */ |
| if (target_read_memory (addr, addr_buf, sizeof addr_buf) != 0) |
| { |
| warning (_("Unable to fetch _dl_debug_addr->.r_brk entry point " |
| "(at address %s) from dynamic linker"), |
| hex_string_custom (addr, 8)); |
| enable_break_failure_warning (); |
| return 0; |
| } |
| addr = extract_unsigned_integer (addr_buf, sizeof addr_buf, byte_order); |
| |
| /* We're done with the loadmap. */ |
| xfree (ldm); |
| |
| /* Remove all the solib event breakpoints. Their addresses |
| may have changed since the last time we ran the program. */ |
| remove_solib_event_breakpoints (); |
| |
| /* Now (finally!) create the solib breakpoint. */ |
| create_solib_event_breakpoint (target_gdbarch (), addr); |
| |
| enable_break2_done = 1; |
| |
| return 1; |
| } |
| |
| /* Tell the user we couldn't set a dynamic linker breakpoint. */ |
| enable_break_failure_warning (); |
| |
| /* Failure return. */ |
| return 0; |
| } |
| |
| static int |
| enable_break (void) |
| { |
| asection *interp_sect; |
| CORE_ADDR entry_point; |
| |
| if (current_program_space->symfile_object_file == NULL) |
| { |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: No symbol file found.\n"); |
| return 0; |
| } |
| |
| if (!entry_point_address_query (&entry_point)) |
| { |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: Symbol file has no entry point.\n"); |
| return 0; |
| } |
| |
| /* Check for the presence of a .interp section. If there is no |
| such section, the executable is statically linked. */ |
| |
| interp_sect = bfd_get_section_by_name (current_program_space->exec_bfd (), |
| ".interp"); |
| |
| if (interp_sect == NULL) |
| { |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: No .interp section found.\n"); |
| return 0; |
| } |
| |
| create_solib_event_breakpoint (target_gdbarch (), entry_point); |
| |
| if (solib_frv_debug) |
| fprintf_unfiltered (gdb_stdlog, |
| "enable_break: solib event breakpoint " |
| "placed at entry point: %s\n", |
| hex_string_custom (entry_point, 8)); |
| return 1; |
| } |
| |
| static void |
| frv_relocate_main_executable (void) |
| { |
| int status; |
| CORE_ADDR exec_addr, interp_addr; |
| struct int_elf32_fdpic_loadmap *ldm; |
| int changed; |
| struct obj_section *osect; |
| |
| status = frv_fdpic_loadmap_addresses (target_gdbarch (), |
| &interp_addr, &exec_addr); |
| |
| if (status < 0 || (exec_addr == 0 && interp_addr == 0)) |
| { |
| /* Not using FDPIC ABI, so do nothing. */ |
| return; |
| } |
| |
| /* Fetch the loadmap located at ``exec_addr''. */ |
| ldm = fetch_loadmap (exec_addr); |
| if (ldm == NULL) |
| error (_("Unable to load the executable's loadmap.")); |
| |
| delete main_executable_lm_info; |
| main_executable_lm_info = new lm_info_frv; |
| 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. */ |
| main_executable_lm_info->got_value = main_got (); |
| } |
| |
| /* Implement the "create_inferior_hook" target_solib_ops method. |
| |
| For the FR-V shared library ABI (FDPIC), the main executable needs |
| to be relocated. The shared library breakpoints also need to be |
| enabled. */ |
| |
| static void |
| frv_solib_create_inferior_hook (int from_tty) |
| { |
| /* Relocate main executable. */ |
| frv_relocate_main_executable (); |
| |
| /* Enable shared library breakpoints. */ |
| if (!enable_break ()) |
| { |
| warning (_("shared library handler failed to enable breakpoint")); |
| return; |
| } |
| } |
| |
| static void |
| frv_clear_solib (void) |
| { |
| lm_base_cache = 0; |
| enable_break2_done = 0; |
| main_lm_addr = 0; |
| |
| delete main_executable_lm_info; |
| main_executable_lm_info = NULL; |
| } |
| |
| static void |
| frv_free_so (struct so_list *so) |
| { |
| lm_info_frv *li = (lm_info_frv *) so->lm_info; |
| |
| delete li; |
| } |
| |
| static void |
| frv_relocate_section_addresses (struct so_list *so, |
| struct target_section *sec) |
| { |
| int seg; |
| lm_info_frv *li = (lm_info_frv *) so->lm_info; |
| int_elf32_fdpic_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; |
| } |
| } |
| } |
| |
| /* Return the GOT address associated with the main executable. Return |
| 0 if it can't be found. */ |
| |
| static CORE_ADDR |
| main_got (void) |
| { |
| struct bound_minimal_symbol got_sym; |
| |
| objfile *objf = current_program_space->symfile_object_file; |
| got_sym = lookup_minimal_symbol ("_GLOBAL_OFFSET_TABLE_", NULL, objf); |
| if (got_sym.minsym == 0) |
| return 0; |
| |
| return BMSYMBOL_VALUE_ADDRESS (got_sym); |
| } |
| |
| /* Find the global pointer for the given function address ADDR. */ |
| |
| CORE_ADDR |
| frv_fdpic_find_global_pointer (CORE_ADDR addr) |
| { |
| for (struct so_list *so : current_program_space->solibs ()) |
| { |
| int seg; |
| lm_info_frv *li = (lm_info_frv *) so->lm_info; |
| int_elf32_fdpic_loadmap *map = li->map; |
| |
| for (seg = 0; seg < map->nsegs; seg++) |
| { |
| if (map->segs[seg].addr <= addr |
| && addr < map->segs[seg].addr + map->segs[seg].p_memsz) |
| return li->got_value; |
| } |
| } |
| |
| /* Didn't find it in any of the shared objects. So assume it's in the |
| main executable. */ |
| return main_got (); |
| } |
| |
| /* Forward declarations for frv_fdpic_find_canonical_descriptor(). */ |
| static CORE_ADDR find_canonical_descriptor_in_load_object |
| (CORE_ADDR, CORE_ADDR, const char *, bfd *, lm_info_frv *); |
| |
| /* Given a function entry point, attempt to find the canonical descriptor |
| associated with that entry point. Return 0 if no canonical descriptor |
| could be found. */ |
| |
| CORE_ADDR |
| frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point) |
| { |
| const char *name; |
| CORE_ADDR addr; |
| CORE_ADDR got_value; |
| struct symbol *sym; |
| |
| /* Fetch the corresponding global pointer for the entry point. */ |
| got_value = frv_fdpic_find_global_pointer (entry_point); |
| |
| /* Attempt to find the name of the function. If the name is available, |
| it'll be used as an aid in finding matching functions in the dynamic |
| symbol table. */ |
| sym = find_pc_function (entry_point); |
| if (sym == 0) |
| name = 0; |
| else |
| name = sym->linkage_name (); |
| |
| /* Check the main executable. */ |
| objfile *objf = current_program_space->symfile_object_file; |
| addr = find_canonical_descriptor_in_load_object |
| (entry_point, got_value, name, objf->obfd, |
| main_executable_lm_info); |
| |
| /* If descriptor not found via main executable, check each load object |
| in list of shared objects. */ |
| if (addr == 0) |
| { |
| for (struct so_list *so : current_program_space->solibs ()) |
| { |
| lm_info_frv *li = (lm_info_frv *) so->lm_info; |
| |
| addr = find_canonical_descriptor_in_load_object |
| (entry_point, got_value, name, so->abfd, li); |
| |
| if (addr != 0) |
| break; |
| } |
| } |
| |
| return addr; |
| } |
| |
| static CORE_ADDR |
| find_canonical_descriptor_in_load_object |
| (CORE_ADDR entry_point, CORE_ADDR got_value, const char *name, bfd *abfd, |
| lm_info_frv *lm) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); |
| arelent *rel; |
| unsigned int i; |
| CORE_ADDR addr = 0; |
| |
| /* Nothing to do if no bfd. */ |
| if (abfd == 0) |
| return 0; |
| |
| /* Nothing to do if no link map. */ |
| if (lm == 0) |
| return 0; |
| |
| /* We want to scan the dynamic relocs for R_FRV_FUNCDESC relocations. |
| (More about this later.) But in order to fetch the relocs, we |
| need to first fetch the dynamic symbols. These symbols need to |
| be cached due to the way that bfd_canonicalize_dynamic_reloc() |
| works. (See the comments in the declaration of struct lm_info |
| for more information.) */ |
| if (lm->dyn_syms == NULL) |
| { |
| long storage_needed; |
| unsigned int number_of_symbols; |
| |
| /* Determine amount of space needed to hold the dynamic symbol table. */ |
| storage_needed = bfd_get_dynamic_symtab_upper_bound (abfd); |
| |
| /* If there are no dynamic symbols, there's nothing to do. */ |
| if (storage_needed <= 0) |
| return 0; |
| |
| /* Allocate space for the dynamic symbol table. */ |
| lm->dyn_syms = (asymbol **) xmalloc (storage_needed); |
| |
| /* Fetch the dynamic symbol table. */ |
| number_of_symbols = bfd_canonicalize_dynamic_symtab (abfd, lm->dyn_syms); |
| |
| if (number_of_symbols == 0) |
| return 0; |
| } |
| |
| /* Fetch the dynamic relocations if not already cached. */ |
| if (lm->dyn_relocs == NULL) |
| { |
| long storage_needed; |
| |
| /* Determine amount of space needed to hold the dynamic relocs. */ |
| storage_needed = bfd_get_dynamic_reloc_upper_bound (abfd); |
| |
| /* Bail out if there are no dynamic relocs. */ |
| if (storage_needed <= 0) |
| return 0; |
| |
| /* Allocate space for the relocs. */ |
| lm->dyn_relocs = (arelent **) xmalloc (storage_needed); |
| |
| /* Fetch the dynamic relocs. */ |
| lm->dyn_reloc_count |
| = bfd_canonicalize_dynamic_reloc (abfd, lm->dyn_relocs, lm->dyn_syms); |
| } |
| |
| /* Search the dynamic relocs. */ |
| for (i = 0; i < lm->dyn_reloc_count; i++) |
| { |
| rel = lm->dyn_relocs[i]; |
| |
| /* Relocs of interest are those which meet the following |
| criteria: |
| |
| - the names match (assuming the caller could provide |
| a name which matches ``entry_point''). |
| - the relocation type must be R_FRV_FUNCDESC. Relocs |
| of this type are used (by the dynamic linker) to |
| look up the address of a canonical descriptor (allocating |
| it if need be) and initializing the GOT entry referred |
| to by the offset to the address of the descriptor. |
| |
| These relocs of interest may be used to obtain a |
| candidate descriptor by first adjusting the reloc's |
| address according to the link map and then dereferencing |
| this address (which is a GOT entry) to obtain a descriptor |
| address. */ |
| if ((name == 0 || strcmp (name, (*rel->sym_ptr_ptr)->name) == 0) |
| && rel->howto->type == R_FRV_FUNCDESC) |
| { |
| gdb_byte buf [FRV_PTR_SIZE]; |
| |
| /* Compute address of address of candidate descriptor. */ |
| addr = rel->address + displacement_from_map (lm->map, rel->address); |
| |
| /* Fetch address of candidate descriptor. */ |
| if (target_read_memory (addr, buf, sizeof buf) != 0) |
| continue; |
| addr = extract_unsigned_integer (buf, sizeof buf, byte_order); |
| |
| /* Check for matching entry point. */ |
| if (target_read_memory (addr, buf, sizeof buf) != 0) |
| continue; |
| if (extract_unsigned_integer (buf, sizeof buf, byte_order) |
| != entry_point) |
| continue; |
| |
| /* Check for matching got value. */ |
| if (target_read_memory (addr + 4, buf, sizeof buf) != 0) |
| continue; |
| if (extract_unsigned_integer (buf, sizeof buf, byte_order) |
| != got_value) |
| continue; |
| |
| /* Match was successful! Exit loop. */ |
| break; |
| } |
| } |
| |
| return addr; |
| } |
| |
| /* Given an objfile, return the address of its link map. This value is |
| needed for TLS support. */ |
| CORE_ADDR |
| frv_fetch_objfile_link_map (struct objfile *objfile) |
| { |
| /* Cause frv_current_sos() to be run if it hasn't been already. */ |
| if (main_lm_addr == 0) |
| solib_add (0, 0, 1); |
| |
| /* frv_current_sos() will set main_lm_addr for the main executable. */ |
| if (objfile == current_program_space->symfile_object_file) |
| return main_lm_addr; |
| |
| /* The other link map addresses may be found by examining the list |
| of shared libraries. */ |
| for (struct so_list *so : current_program_space->solibs ()) |
| { |
| lm_info_frv *li = (lm_info_frv *) so->lm_info; |
| |
| if (so->objfile == objfile) |
| return li->lm_addr; |
| } |
| |
| /* Not found! */ |
| return 0; |
| } |
| |
| struct target_so_ops frv_so_ops; |
| |
| void _initialize_frv_solib (); |
| void |
| _initialize_frv_solib () |
| { |
| frv_so_ops.relocate_section_addresses = frv_relocate_section_addresses; |
| frv_so_ops.free_so = frv_free_so; |
| frv_so_ops.clear_solib = frv_clear_solib; |
| frv_so_ops.solib_create_inferior_hook = frv_solib_create_inferior_hook; |
| frv_so_ops.current_sos = frv_current_sos; |
| frv_so_ops.open_symbol_file_object = open_symbol_file_object; |
| frv_so_ops.in_dynsym_resolve_code = frv_in_dynsym_resolve_code; |
| frv_so_ops.bfd_open = solib_bfd_open; |
| |
| /* Debug this file's internals. */ |
| add_setshow_zuinteger_cmd ("solib-frv", class_maintenance, |
| &solib_frv_debug, _("\ |
| Set internal debugging of shared library code for FR-V."), _("\ |
| Show internal debugging of shared library code for FR-V."), _("\ |
| When non-zero, FR-V solib specific internal debugging is enabled."), |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setdebuglist, &showdebuglist); |
| } |