diff options
-rw-r--r-- | gdb/bfin-tdep.c | 87 | ||||
-rw-r--r-- | gdb/bfin-tdep.h | 3 | ||||
-rw-r--r-- | gdb/configure.tgt | 3 | ||||
-rw-r--r-- | gdb/frv-tdep.c | 44 | ||||
-rw-r--r-- | gdb/frv-tdep.h | 25 | ||||
-rw-r--r-- | gdb/solib-bfin.c | 31 | ||||
-rw-r--r-- | gdb/solib-fdpic.c | 1182 | ||||
-rw-r--r-- | gdb/solib-fdpic.h | 72 | ||||
-rw-r--r-- | gdb/solib-frv.c | 1143 |
9 files changed, 1391 insertions, 1199 deletions
diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c index f751e27..948efee 100644 --- a/gdb/bfin-tdep.c +++ b/gdb/bfin-tdep.c @@ -37,6 +37,8 @@ #include "elf/bfin.h" #include "osabi.h" #include "infcall.h" +#include "solib.h" +#include "solib-fdpic.h" #include "xml-syscall.h" #include "bfin-tdep.h" @@ -492,6 +494,58 @@ bfin_register_type (struct gdbarch *gdbarch, int regnum) } static CORE_ADDR +find_func_descr (struct gdbarch *gdbarch, CORE_ADDR entry_point) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + CORE_ADDR descr; + char valbuf[4]; + + descr = fdpic_find_canonical_descriptor (entry_point); + + if (descr != 0) + return descr; + + /* Construct a non-canonical descriptor from space allocated on + the stack. */ + + descr = value_as_long (value_allocate_space_in_inferior (8)); + store_unsigned_integer (valbuf, 4, byte_order, entry_point); + write_memory (descr, valbuf, 4); + store_unsigned_integer (valbuf, 4, byte_order, + fdpic_find_global_pointer (entry_point)); + write_memory (descr + 4, valbuf, 4); + return descr; +} + +static CORE_ADDR +bfin_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr, + struct target_ops *targ) +{ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + CORE_ADDR entry_point; + CORE_ADDR got_address; + + entry_point = get_target_memory_unsigned (targ, addr, 4, byte_order); + got_address = get_target_memory_unsigned (targ, addr + 4, 4, byte_order); + + if (got_address == fdpic_find_global_pointer (entry_point)) + return entry_point; + else + return addr; +} + +static CORE_ADDR +bfin_convert_from_addr_func_ptr (struct gdbarch *gdbarch, CORE_ADDR addr, + struct target_ops *targ) +{ + CORE_ADDR descr; + + descr = find_func_descr (gdbarch, addr); + + return descr; +} + +static CORE_ADDR bfin_push_dummy_call (struct gdbarch *gdbarch, struct value *function, struct regcache *regcache, @@ -552,6 +606,14 @@ bfin_push_dummy_call (struct gdbarch *gdbarch, regcache_cooked_write_unsigned (regcache, BFIN_RETS_REGNUM, bp_addr); + if (abi == BFIN_ABI_FDPIC) + { + /* Set the GOT register for the FDPIC ABI. */ + regcache_cooked_write_unsigned + (regcache, BFIN_P3_REGNUM, + fdpic_find_global_pointer (func_addr)); + } + /* Finally, update the stack pointer. */ regcache_cooked_write_unsigned (regcache, BFIN_SP_REGNUM, sp); @@ -779,9 +841,19 @@ static struct gdbarch * bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) { struct gdbarch *gdbarch; + int elf_flags; enum bfin_abi abi; - abi = BFIN_ABI_FLAT; + /* Extract the ELF flags, if available. */ + if (info.abfd && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + elf_flags = elf_elfheader (info.abfd)->e_flags; + else + elf_flags = 0; + + if (elf_flags & EF_BFIN_FDPIC) + abi = BFIN_ABI_FDPIC; + else + abi = BFIN_ABI_FLAT; /* If there is already a candidate, use it. */ @@ -824,6 +896,8 @@ bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_frame_args_skip (gdbarch, 8); set_gdbarch_frame_align (gdbarch, bfin_frame_align); + set_gdbarch_so_ops (gdbarch, &fdpic_so_ops); + /* Hook in ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); @@ -833,6 +907,17 @@ bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) frame_unwind_append_unwinder (gdbarch, &bfin_frame_unwind); + if (bfin_abi (gdbarch) == BFIN_ABI_FDPIC) + { + set_gdbarch_convert_from_func_ptr_addr (gdbarch, + bfin_convert_from_func_ptr_addr); + set_gdbarch_convert_from_addr_func_ptr (gdbarch, + bfin_convert_from_addr_func_ptr); + } + + if (bfin_abi (gdbarch) == BFIN_ABI_FDPIC) + set_gdbarch_use_get_offsets (gdbarch, 0); + return gdbarch; } diff --git a/gdb/bfin-tdep.h b/gdb/bfin-tdep.h index 2671496..e94a214 100644 --- a/gdb/bfin-tdep.h +++ b/gdb/bfin-tdep.h @@ -92,7 +92,8 @@ enum gdb_regnum { /* The ABIs for Blackfin. */ enum bfin_abi { - BFIN_ABI_FLAT + BFIN_ABI_FLAT, + BFIN_ABI_FDPIC }; /* Target-dependent structure in gdbarch. */ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index e84e222..a6cb35f 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -210,7 +210,8 @@ avr-*-*) bfin-*-*linux*) # Target: Blackfin Linux - gdb_target_obs="bfin-tdep.o bfin-linux-tdep.o linux-tdep.o" + gdb_target_obs="bfin-tdep.o bfin-linux-tdep.o linux-tdep.o + solib.o solib-bfin.o" ;; bfin-*-*) # Target: Blackfin processor diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c index 056aad3..9fbfaa5 100644 --- a/gdb/frv-tdep.c +++ b/gdb/frv-tdep.c @@ -36,6 +36,7 @@ #include "osabi.h" #include "infcall.h" #include "solib.h" +#include "solib-fdpic.h" #include "frv-tdep.h" #include "objfiles.h" #include "gdbarch.h" @@ -97,37 +98,6 @@ frv_abi (struct gdbarch *gdbarch) return tdep->frv_abi; } -/* Fetch the interpreter and executable loadmap addresses (for shared - library support) for the FDPIC ABI. Return 0 if successful, -1 if - not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ -int -frv_fdpic_loadmap_addresses (struct gdbarch *gdbarch, CORE_ADDR *interp_addr, - CORE_ADDR *exec_addr) -{ - if (frv_abi (gdbarch) != FRV_ABI_FDPIC) - return -1; - else - { - struct regcache *regcache = get_current_regcache (); - - if (interp_addr != NULL) - { - ULONGEST val; - regcache_cooked_read_unsigned (regcache, - fdpic_loadmap_interp_regnum, &val); - *interp_addr = val; - } - if (exec_addr != NULL) - { - ULONGEST val; - regcache_cooked_read_unsigned (regcache, - fdpic_loadmap_exec_regnum, &val); - *exec_addr = val; - } - return 0; - } -} - /* Allocate a new variant structure, and set up default values for all the fields. */ static frv_gdbarch_tdep * @@ -1146,7 +1116,7 @@ find_func_descr (struct gdbarch *gdbarch, CORE_ADDR entry_point) || entry_point != start_addr) return entry_point; - descr = frv_fdpic_find_canonical_descriptor (entry_point); + descr = fdpic_find_canonical_descriptor (entry_point); if (descr != 0) return descr; @@ -1158,7 +1128,7 @@ find_func_descr (struct gdbarch *gdbarch, CORE_ADDR entry_point) store_unsigned_integer (valbuf, 4, byte_order, entry_point); write_memory (descr, valbuf, 4); store_unsigned_integer (valbuf, 4, byte_order, - frv_fdpic_find_global_pointer (entry_point)); + fdpic_find_global_pointer (entry_point)); write_memory (descr + 4, valbuf, 4); return descr; } @@ -1174,7 +1144,7 @@ frv_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr, entry_point = get_target_memory_unsigned (targ, addr, 4, byte_order); got_address = get_target_memory_unsigned (targ, addr + 4, 4, byte_order); - if (got_address == frv_fdpic_find_global_pointer (entry_point)) + if (got_address == fdpic_find_global_pointer (entry_point)) return entry_point; else return addr; @@ -1299,7 +1269,7 @@ frv_push_dummy_call (struct gdbarch *gdbarch, struct value *function, /* Set the GOT register for the FDPIC ABI. */ regcache_cooked_write_unsigned (regcache, first_gpr_regnum + 15, - frv_fdpic_find_global_pointer (func_addr)); + fdpic_find_global_pointer (func_addr)); } /* Finally, update the SP register. */ @@ -1548,7 +1518,7 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_convert_from_func_ptr_addr (gdbarch, frv_convert_from_func_ptr_addr); - set_gdbarch_so_ops (gdbarch, &frv_so_ops); + set_gdbarch_so_ops (gdbarch, &fdpic_so_ops); /* Hook in ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); @@ -1558,7 +1528,7 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, - frv_fetch_objfile_link_map); + fdpic_fetch_objfile_link_map); return gdbarch; } diff --git a/gdb/frv-tdep.h b/gdb/frv-tdep.h index 037d81a..842b2e8 100644 --- a/gdb/frv-tdep.h +++ b/gdb/frv-tdep.h @@ -95,30 +95,9 @@ enum { frv_num_pseudo_regs = last_pseudo_regnum - first_pseudo_regnum + 1, }; +struct gdbarch; + /* Return the FR-V ABI associated with GDBARCH. */ enum frv_abi frv_abi (struct gdbarch *gdbarch); -/* Fetch the interpreter and executable loadmap addresses (for shared - library support) for the FDPIC ABI. Return 0 if successful, -1 if - not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ -int frv_fdpic_loadmap_addresses (struct gdbarch *gdbarch, - CORE_ADDR *interp_addr, CORE_ADDR *exec_addr); - -/* Given a function entry point, find and return the GOT address for the - containing load module. */ -CORE_ADDR frv_fdpic_find_global_pointer (CORE_ADDR addr); - -/* Given a function entry point, find and return the canonical descriptor - for that function, if one exists. If no canonical descriptor could - be found, return 0. */ -CORE_ADDR frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point); - - -/* 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); - -struct target_so_ops; -extern const struct target_so_ops frv_so_ops; - #endif /* FRV_TDEP_H */ diff --git a/gdb/solib-bfin.c b/gdb/solib-bfin.c new file mode 100644 index 0000000..e380766 --- /dev/null +++ b/gdb/solib-bfin.c @@ -0,0 +1,31 @@ +/* Handle Blackfin (FDPIC) shared libraries for GDB, the GNU Debugger. + Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010 + 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 "solib-fdpic.h" +#include "bfin-tdep.h" +#include "elf/bfin.h" + +#define arch_abi bfin_abi +#define ARCH_ABI_FDPIC BFIN_ABI_FDPIC +#define R_ARCH_FUNCDESC R_BFIN_FUNCDESC +#define FDPIC_INTERP_REGNUM BFIN_FDPIC_INTERP_REGNUM +#define FDPIC_EXEC_REGNUM BFIN_FDPIC_EXEC_REGNUM +#define LINKER_SYM(sym) "_"sym + +#include "solib-fdpic.c" diff --git a/gdb/solib-fdpic.c b/gdb/solib-fdpic.c new file mode 100644 index 0000000..301f90d --- /dev/null +++ b/gdb/solib-fdpic.c @@ -0,0 +1,1182 @@ +/* Handle FDPIC shared libraries for GDB, the GNU Debugger. + Copyright (C) 2004-2016 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 "solib-fdpic.h" +#include "gdb_bfd.h" + +#ifndef LINKER_SYM +# define LINKER_SYM(sym) sym +#endif + +/* Flag which indicates whether internal debug messages should be printed. */ +static unsigned int solib_fdpic_debug; + +/* Pointers are four bytes wide. */ +enum { FDPIC_PTR_SIZE = 4 }; + +/* Representation of loadmap and related structs for the 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_fdpic : public lm_info_base +{ + ~lm_info_fdpic () + { + 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 fdpic_fetch_objfile_link_map(). */ + CORE_ADDR lm_addr = 0; + + /* Cached dynamic symbol table and dynamic relocs initialized and + used only by fdpic_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 fdpic_relocate_main_executable(). */ +static lm_info_fdpic *main_executable_lm_info; + +static void fdpic_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 FDPIC, 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[FDPIC_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_fdpic_debug) + gdb_printf (gdb_stdlog, + "lm_base: _GLOBAL_OFFSET_TABLE_ not found.\n"); + return 0; + } + + addr = got_sym.value_address () + 8; + + if (solib_fdpic_debug) + gdb_printf (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_fdpic_debug) + gdb_printf (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 * +fdpic_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, + fdpic_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) + fdpic_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_fdpic_debug) + gdb_printf (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 (_("fdpic_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 (_("fdpic_current_sos: Unable to fetch load map. " + "Shared object chain may be incomplete.")); + break; + } + + sop = XCNEW (struct so_list); + lm_info_fdpic *li = new lm_info_fdpic; + 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, &name_buf, SO_NAME_MAX_PATH_SIZE - 1); + + if (solib_fdpic_debug) + gdb_printf (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; +} + + +/* Fetch the interpreter and executable loadmap addresses (for shared + library support) for the FDPIC ABI. Return 0 if successful, -1 if + not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ +int +fdpic_loadmap_addresses (struct gdbarch *gdbarch, CORE_ADDR *interp_addr, + CORE_ADDR *exec_addr) +{ + if (arch_abi (gdbarch) != ARCH_ABI_FDPIC) + return -1; + else + { + struct regcache *regcache = get_current_regcache (); + + if (interp_addr != NULL) + { + ULONGEST val; + regcache_cooked_read_unsigned (regcache, + FDPIC_INTERP_REGNUM, &val); + *interp_addr = val; + } + if (exec_addr != NULL) + { + ULONGEST val; + regcache_cooked_read_unsigned (regcache, + FDPIC_EXEC_REGNUM, &val); + *exec_addr = val; + } + return 0; + } +} + + +/* 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 +fdpic_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 +fdpic_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. + + 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[FDPIC_PTR_SIZE]; + struct int_elf32_fdpic_loadmap *ldm; + char sym_dl_debug_addr[] = LINKER_SYM("_dl_debug_addr"); + + /* 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 = 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_fdpic_debug) + gdb_printf (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 + += fdpic_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 + += fdpic_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, sym_dl_debug_addr); + if (addr == 0) + { + warning (_("Could not find symbol %s in dynamic linker"), + sym_dl_debug_addr); + enable_break_failure_warning (); + return 0; + } + + if (solib_fdpic_debug) + gdb_printf (gdb_stdlog, + "enable_break: %s (prior to relocation) = %s\n", + sym_dl_debug_addr, hex_string_custom (addr, 8)); + + addr += fdpic_displacement_from_map (ldm, addr); + + if (solib_fdpic_debug) + gdb_printf (gdb_stdlog, + enable_break: %s (after relocation) = %s\n", + sym_dl_debug_addr, 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 %s (at address %s) from " + "dynamic linker"), + sym_dl_debug_addr, hex_string_custom (addr, 8)); + } + addr = extract_unsigned_integer (addr_buf, sizeof addr_buf, byte_order); + + if (solib_fdpic_debug) + gdb_printf (gdb_stdlog, + "enable_break: %s[0..3] = %s\n", + sym_dl_debug_addr, + 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_fdpic_debug) + gdb_printf (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 %s->r_brk (at address %s) " + "from dynamic linker"), + sym_dl_debug_addr, 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 %s->.r_brk entry point (at address %s) " + "from dynamic linker"), + sym_dl_debug_addr, 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_fdpic_debug) + gdb_printf (gdb_stdlog, + "enable_break: No symbol file found.\n"); + return 0; + } + + if (!entry_point_address_query (&entry_point)) + { + if (solib_fdpic_debug) + gdb_printf (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_fdpic_debug) + gdb_printf (gdb_stdlog, + "enable_break: No .interp section found.\n"); + return 0; + } + + create_solib_event_breakpoint (target_gdbarch (), entry_point); + + if (solib_fdpic_debug) + gdb_printf (gdb_stdlog, + "enable_break: solib event breakpoint " + "placed at entry point: %s\n", + hex_string_custom (entry_point, 8)); + return 1; +} + +static void +fdpic_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 = 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_fdpic; + main_executable_lm_info->map = ldm; + + objfile *objf = current_program_space->symfile_object_file; + section_offsets new_offsets (objf->section_offsets.size ()); + old_chain = make_cleanup (xfree, new_offsets); + 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); + + do_cleanups (old_chain); + + /* 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 FDPIC shared library ABI, the main executable needs + to be relocated. The shared library breakpoints also need + to be enabled. */ + +static void +fdpic_solib_create_inferior_hook (int from_tty) +{ + /* Relocate main executable. */ + fdpic_relocate_main_executable (); + + /* Enable shared library breakpoints. */ + if (!enable_break ()) + { + warning (_("shared library handler failed to enable breakpoint")); + return; + } +} + +static void +fdpic_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 +fdpic_free_so (struct so_list *so) +{ + lm_info_fdpic *li = (lm_info_fdpic *) so->lm_info; + + delete li; +} + +static void +fdpic_relocate_section_addresses (struct so_list *so, + struct target_section *sec) +{ + int seg; + lm_info_fdpic *li = (lm_info_fdpic *) 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 got_sym.value_address (); +} + +/* Find the global pointer for the given function address ADDR. */ + +CORE_ADDR +fdpic_find_global_pointer (CORE_ADDR addr) +{ + for (struct so_list *so : current_program_space->solibs ()) + { + int seg; + lm_info_fdpic *li = (lm_info_fdpic *) 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 fdpic_find_canonical_descriptor(). */ +static CORE_ADDR fdpic_find_canonical_descriptor_in_load_object + (CORE_ADDR, CORE_ADDR, const char *, bfd *, lm_info_fdpic *); + +/* 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 +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 = 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 = fdpic_find_canonical_descriptor_in_load_object + (entry_point, got_value, name, objf->obfd.get (), + 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_fdpic *li = (lm_info_fdpic *) so->lm_info; + + addr = fdpic_find_canonical_descriptor_in_load_object + (entry_point, got_value, name, so->abfd, li); + + if (addr != 0) + break; + } + } + + return addr; +} + +static CORE_ADDR +fdpic_find_canonical_descriptor_in_load_object + (CORE_ADDR entry_point, CORE_ADDR got_value, const char *name, bfd *abfd, + lm_info_fdpic *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_ARCH_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 lm_info_fdpic + 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_ARCH_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_ARCH_FUNCDESC) + { + gdb_byte buf [FDPIC_PTR_SIZE]; + + /* Compute address of address of candidate descriptor. */ + addr = rel->address + + fdpic_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 +fdpic_fetch_objfile_link_map (struct objfile *objfile) +{ + /* Cause fdpic_current_sos() to be run if it hasn't been already. */ + if (main_lm_addr == 0) + solib_add (0, 0, 1); + + /* fdpic_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_fdpic *li = (lm_info_fdpic *) so->lm_info; + + if (so->objfile == objfile) + return li->lm_addr; + } + + /* Not found! */ + return 0; +} + +const struct target_so_ops fdpic_so_ops = { + fdpic_relocate_section_addresses, + fdpic_free_so, + nullptr, + fdpic_clear_solib, + fdpic_solib_create_inferior_hook, + fdpic_current_sos, + open_symbol_file_object, + fdpic_in_dynsym_resolve_code, + solib_bfd_open, +}; + +void _initialize_fdpic_solib (); +void +_initialize_fdpic_solib () +{ + /* Debug this file's internals. */ + add_setshow_zuinteger_cmd ("solib-fdpic", class_maintenance, + &solib_fdpic_debug, _("\ +Set internal debugging of shared library code for FDPIC."), _("\ +Show internal debugging of shared library code for FDPIC."), _("\ +When non-zero, FDPIC solib specific internal debugging is enabled."), + NULL, + NULL, /* FIXME: i18n: */ + &setdebuglist, &showdebuglist); +} diff --git a/gdb/solib-fdpic.h b/gdb/solib-fdpic.h new file mode 100644 index 0000000..80b361a --- /dev/null +++ b/gdb/solib-fdpic.h @@ -0,0 +1,72 @@ +/* Handle FDPIC shared libraries for GDB, the GNU Debugger. + Copyright (C) 2004, 2007, 2008, 2009, 2010 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/>. */ + +#ifndef SOLIB_FDPIC_H +#define SOLIB_FDPIC_H + +#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 "regcache.h" + +/* Targets implementing FDPIC shared libraries should define: + + arch_abi (required) + + ARCH_ABI_FDPIC (required) + + R_ARCH_FUNCDESC (required) + + FDPIC_EXEC_REGNUM (required) + + FDPIC_INTERP_REGNUM (required) + + LINKER_SYM (optional) + + */ + +/* Given a function entry point, find and return the GOT address for the + containing load module. */ +CORE_ADDR fdpic_find_global_pointer (CORE_ADDR addr); + +/* Given a function entry point, find and return the canonical descriptor + for that function, if one exists. If no canonical descriptor could + be found, return 0. */ +CORE_ADDR fdpic_find_canonical_descriptor (CORE_ADDR entry_point); + +/* Given an objfile, return the address of its link map. This value is + needed for TLS support. */ +CORE_ADDR fdpic_fetch_objfile_link_map (struct objfile *objfile); + +/* Fetch the interpreter and executable loadmap addresses (for shared + library support) for the FDPIC ABI. Return 0 if successful, -1 if + not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ +int fdpic_loadmap_addresses (struct gdbarch *gdbarch, CORE_ADDR *interp_addr, + CORE_ADDR *exec_addr); + +struct target_so_ops; +extern const struct target_so_ops fdpic_so_ops; + +#endif diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c index 6ca303c..d1ef6b2 100644 --- a/gdb/solib-frv.c +++ b/gdb/solib-frv.c @@ -16,1143 +16,14 @@ 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 "solib-fdpic.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) - gdb_printf (gdb_stdlog, - "lm_base: _GLOBAL_OFFSET_TABLE_ not found.\n"); - return 0; - } - - addr = got_sym.value_address () + 8; - - if (solib_frv_debug) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (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) - gdb_printf (gdb_stdlog, - "enable_break: No symbol file found.\n"); - return 0; - } - - if (!entry_point_address_query (&entry_point)) - { - if (solib_frv_debug) - gdb_printf (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) - gdb_printf (gdb_stdlog, - "enable_break: No .interp section found.\n"); - return 0; - } - - create_solib_event_breakpoint (target_gdbarch (), entry_point); - - if (solib_frv_debug) - gdb_printf (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 got_sym.value_address (); -} - -/* 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.get (), - 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; -} -const struct target_so_ops frv_so_ops = -{ - frv_relocate_section_addresses, - frv_free_so, - nullptr, - frv_clear_solib, - frv_solib_create_inferior_hook, - frv_current_sos, - open_symbol_file_object, - frv_in_dynsym_resolve_code, - solib_bfd_open, -}; +#define arch_abi frv_abi +#define ARCH_ABI_FDPIC FRV_ABI_FDPIC +#define R_ARCH_FUNCDESC R_FRV_FUNCDESC +#define FDPIC_INTERP_REGNUM fdpic_loadmap_interp_regnum +#define FDPIC_EXEC_REGNUM fdpic_loadmap_exec_regnum -void _initialize_frv_solib (); -void -_initialize_frv_solib () -{ - /* 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); -} +#include "solib-fdpic.c" |