From f830ed7d1b3289c2a7d9da4fd92dd667ff3bb3f2 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 19 Aug 2016 01:11:16 +0100 Subject: [PATCH 1/2] Introduce linux_solib_ops Add a custom solib_ops for Linux-based targets that extends solib-svr4.c and then adds vDSO awareness to a couple solib_ops methods. This replaces the current scheme of including a bit of vDSO-awareness in solib-svr4.c directly, but hidden (though not very well) behind the gdbarch_vsyscall_range gdbarch hook. The idea is to read the vDSO out of memory before current_sos() is ever called, and the scheme doesn't allow that in a clean way. This change allows centralizing all vDSO handling in linux-tdep.c, and completely getting rid of that gdbarch hook. symfile-mem.c:add_vsyscall_page could move to linux-tdep.c too, but I have not done that yet. I'd like to do that though in the future, and also do a global s/vsyscall/vDSO/g throughout, since we're really handling the vDSO, not the old vsyscall. All the vsyscall references are probably making all this code quite confusing to newcomers, I bet. --- gdb/gdbarch.c | 23 ------ gdb/gdbarch.h | 9 --- gdb/gdbarch.sh | 6 -- gdb/linux-tdep.c | 134 +++++++++++++++++++++++++++++++++-- gdb/linux-tdep.h | 4 ++ gdb/mips-linux-tdep.c | 2 +- gdb/ppc-linux-tdep.c | 2 +- gdb/solib-spu.c | 18 ++--- gdb/solib-svr4.c | 190 +++++++++++++++----------------------------------- gdb/solib-svr4.h | 21 ++++++ gdb/solib.c | 2 +- gdb/solib.h | 4 ++ gdb/symfile-mem.c | 38 ++++------ gdb/symfile-mem.h | 30 ++++++++ 14 files changed, 269 insertions(+), 214 deletions(-) create mode 100644 gdb/symfile-mem.h diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index af7359e..a07029e 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -330,7 +330,6 @@ struct gdbarch gdbarch_insn_is_jump_ftype *insn_is_jump; gdbarch_auxv_parse_ftype *auxv_parse; gdbarch_print_auxv_entry_ftype *print_auxv_entry; - gdbarch_vsyscall_range_ftype *vsyscall_range; gdbarch_infcall_mmap_ftype *infcall_mmap; gdbarch_infcall_munmap_ftype *infcall_munmap; gdbarch_gcc_target_options_ftype *gcc_target_options; @@ -435,7 +434,6 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->insn_is_ret = default_insn_is_ret; gdbarch->insn_is_jump = default_insn_is_jump; gdbarch->print_auxv_entry = default_print_auxv_entry; - gdbarch->vsyscall_range = default_vsyscall_range; gdbarch->infcall_mmap = default_infcall_mmap; gdbarch->infcall_munmap = default_infcall_munmap; gdbarch->gcc_target_options = default_gcc_target_options; @@ -682,7 +680,6 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of insn_is_jump, invalid_p == 0 */ /* Skip verify of auxv_parse, has predicate. */ /* Skip verify of print_auxv_entry, invalid_p == 0 */ - /* Skip verify of vsyscall_range, invalid_p == 0 */ /* Skip verify of infcall_mmap, invalid_p == 0 */ /* Skip verify of infcall_munmap, invalid_p == 0 */ /* Skip verify of gcc_target_options, invalid_p == 0 */ @@ -1423,9 +1420,6 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: virtual_frame_pointer = <%s>\n", host_address_to_string (gdbarch->virtual_frame_pointer)); fprintf_unfiltered (file, - "gdbarch_dump: vsyscall_range = <%s>\n", - host_address_to_string (gdbarch->vsyscall_range)); - fprintf_unfiltered (file, "gdbarch_dump: vtable_function_descriptors = %s\n", plongest (gdbarch->vtable_function_descriptors)); fprintf_unfiltered (file, @@ -4793,23 +4787,6 @@ set_gdbarch_print_auxv_entry (struct gdbarch *gdbarch, gdbarch->print_auxv_entry = print_auxv_entry; } -int -gdbarch_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) -{ - gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->vsyscall_range != NULL); - if (gdbarch_debug >= 2) - fprintf_unfiltered (gdb_stdlog, "gdbarch_vsyscall_range called\n"); - return gdbarch->vsyscall_range (gdbarch, range); -} - -void -set_gdbarch_vsyscall_range (struct gdbarch *gdbarch, - gdbarch_vsyscall_range_ftype vsyscall_range) -{ - gdbarch->vsyscall_range = vsyscall_range; -} - CORE_ADDR gdbarch_infcall_mmap (struct gdbarch *gdbarch, CORE_ADDR size, unsigned prot) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index bc0f692..8c7a37b 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -1471,15 +1471,6 @@ typedef void (gdbarch_print_auxv_entry_ftype) (struct gdbarch *gdbarch, struct u extern void gdbarch_print_auxv_entry (struct gdbarch *gdbarch, struct ui_file *file, CORE_ADDR type, CORE_ADDR val); extern void set_gdbarch_print_auxv_entry (struct gdbarch *gdbarch, gdbarch_print_auxv_entry_ftype *print_auxv_entry); -/* Find the address range of the current inferior's vsyscall/vDSO, and - write it to *RANGE. If the vsyscall's length can't be determined, a - range with zero length is returned. Returns true if the vsyscall is - found, false otherwise. */ - -typedef int (gdbarch_vsyscall_range_ftype) (struct gdbarch *gdbarch, struct mem_range *range); -extern int gdbarch_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range); -extern void set_gdbarch_vsyscall_range (struct gdbarch *gdbarch, gdbarch_vsyscall_range_ftype *vsyscall_range); - /* Allocate SIZE bytes of PROT protected page aligned memory in inferior. PROT has GDB_MMAP_PROT_* bitmask format. Throw an error if it is not possible. Returned address is always valid. */ diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index d8e0eeb..8f5a893 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -1114,12 +1114,6 @@ M:int:auxv_parse:gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_AD # to FILE. m:void:print_auxv_entry:struct ui_file *file, CORE_ADDR type, CORE_ADDR val:file, type, val::default_print_auxv_entry::0 -# Find the address range of the current inferior's vsyscall/vDSO, and -# write it to *RANGE. If the vsyscall's length can't be determined, a -# range with zero length is returned. Returns true if the vsyscall is -# found, false otherwise. -m:int:vsyscall_range:struct mem_range *range:range::default_vsyscall_range::0 - # Allocate SIZE bytes of PROT protected page aligned memory in inferior. # PROT has GDB_MMAP_PROT_* bitmask format. # Throw an error if it is not possible. Returned address is always valid. diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index ab110b0..91931eb 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -38,9 +38,17 @@ #include "gdbcmd.h" #include "gdb_regex.h" #include "common/enum-flags.h" +#include "solib.h" +#include "solist.h" +#include "solib-svr4.h" +#include "symfile-mem.h" #include +/* Shared library operations for Linux. This inherits svr4, and adds + support for the vDSO. */ +struct target_so_ops linux_so_ops; + /* This enum represents the values that the user can choose when informing the Linux kernel about which memory mappings will be dumped in a corefile. They are described in the file @@ -2343,11 +2351,15 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range) return 0; } -/* Implementation of the "vsyscall_range" gdbarch hook. Handles - caching, and defers the real work to linux_vsyscall_range_raw. */ +/* Find the address range of the current inferior's vDSO. If the + vDSO's length can't be determined, a range with zero length is + recorded. Returns true if the vDSO is found, false otherwise. + + Handles caching in struct linux_info, and defers the real work to + linux_vsyscall_range_raw. */ static int -linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) +linux_vsyscall_range (struct gdbarch *gdbarch) { struct linux_info *info = get_linux_inferior_data (); @@ -2361,9 +2373,8 @@ linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) if (info->vsyscall_range_p < 0) return 0; - - *range = info->vsyscall_range; - return 1; + else + return 1; } /* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system @@ -2484,6 +2495,107 @@ show_use_coredump_filter (struct ui_file *file, int from_tty, " corefiles is %s.\n"), value); } +/* Implement the "create_inferior_hook" target_so_ops method. */ + +static void +linux_solib_create_inferior_hook (int from_tty) +{ + struct linux_info *info = get_linux_inferior_data (); + + if (linux_vsyscall_range (target_gdbarch ())) + { + /* Add the vDSO symbols. We need to do this before calling the + svr4 version, because that refreshes the DSO list. In some + cases, such as when debugging core dumps, the information + necessary for identifying the vDSO in order to filter it out + can only be found by reading the vDSO out of memory. + + Note we don't pass SYMFILE_VERBOSE (i.e., pass FROM_TTY==0), + because the action of loading the vDSO was not triggered by + the user, even if the user typed "run" at the TTY. */ + add_vsyscall_page (&info->vsyscall_range, SYMFILE_DEFER_BP_RESET); + } + + svr4_so_ops.solib_create_inferior_hook (from_tty); +} + +/* Implement the "current_sos" target_so_ops method. */ + +static struct so_list * +linux_current_sos (void) +{ + struct so_list *so_head = svr4_so_ops.current_sos (); + struct linux_info *info = get_linux_inferior_data (); + + /* Filter out the vDSO module, if present. Its symbol file would + not be found on disk. The vDSO/vsyscall's OBJFILE is instead + managed by symfile-mem.c:add_vsyscall_page. */ + if (linux_vsyscall_range (target_gdbarch ()) + && info->vsyscall_range.length != 0) + { + struct so_list **sop; + + sop = &so_head; + while (*sop != NULL) + { + struct so_list *so = *sop; + struct svr4_lm_info *lm_info = (struct svr4_lm_info *) so->lm_info; + + /* We can't simply match the vDSO by starting address alone, + because lm_info->l_addr_inferior (and also l_addr) do not + necessarily represent the real starting address of the + ELF if the vDSO's ELF itself is "prelinked". The l_ld + field (the ".dynamic" section of the shared object) + always points at the absolute/resolved address though. + So check whether that address is inside the vDSO's + mapping instead. + + E.g., on Linux 3.16 (x86_64) the vDSO is a regular + 0-based ELF, and we see: + + (gdb) info auxv + 33 AT_SYSINFO_EHDR System-supplied DSO's ELF header 0x7ffff7ffb000 + (gdb) p/x *_r_debug.r_map.l_next + $1 = {l_addr = 0x7ffff7ffb000, ..., l_ld = 0x7ffff7ffb318, ...} + + And on Linux 2.6.32 (x86_64) we see: + + (gdb) info auxv + 33 AT_SYSINFO_EHDR System-supplied DSO's ELF header 0x7ffff7ffe000 + (gdb) p/x *_r_debug.r_map.l_next + $5 = {l_addr = 0x7ffff88fe000, ..., l_ld = 0x7ffff7ffe580, ... } + + Dumping that vDSO shows: + + (gdb) info proc mappings + 0x7ffff7ffe000 0x7ffff7fff000 0x1000 0 [vdso] + (gdb) dump memory vdso.bin 0x7ffff7ffe000 0x7ffff7fff000 + # readelf -Wa vdso.bin + [...] + Entry point address: 0xffffffffff700700 + [...] + Section Headers: + [Nr] Name Type Address Off Size + [ 0] NULL 0000000000000000 000000 000000 + [ 1] .hash HASH ffffffffff700120 000120 000038 + [ 2] .dynsym DYNSYM ffffffffff700158 000158 0000d8 + [...] + [ 9] .dynamic DYNAMIC ffffffffff700580 000580 0000f0 + */ + if (address_in_mem_range (lm_info->l_ld, &info->vsyscall_range)) + { + *sop = so->next; + free_so (so); + break; + } + + sop = &so->next; + } + } + + return so_head; +} + /* To be called from the various GDB_OSABI_LINUX handlers for the various GNU/Linux architectures and machine types. */ @@ -2501,10 +2613,18 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) linux_gdb_signal_from_target); set_gdbarch_gdb_signal_to_target (gdbarch, linux_gdb_signal_to_target); - set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range); set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap); set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap); set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); + + /* Inherit svr4 and override a few functions. */ + if (linux_so_ops.current_sos == NULL) + { + linux_so_ops = svr4_so_ops; + linux_so_ops.current_sos = linux_current_sos; + linux_so_ops.solib_create_inferior_hook = linux_solib_create_inferior_hook; + } + set_solib_ops (gdbarch, &linux_so_ops); } /* Provide a prototype to silence -Wmissing-prototypes. */ diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 2da7de4..da08097 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -22,6 +22,10 @@ #include "bfd.h" +struct target_so_ops; + +extern struct target_so_ops linux_so_ops; + struct regcache; /* Enum used to define the extra fields of the siginfo type used by an diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c index 8dc0566..78eb856 100644 --- a/gdb/mips-linux-tdep.c +++ b/gdb/mips-linux-tdep.c @@ -1716,7 +1716,7 @@ mips_linux_init_abi (struct gdbarch_info info, dependency on solib-svr4.c's _initialize routine. */ if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL) { - mips_svr4_so_ops = svr4_so_ops; + mips_svr4_so_ops = linux_so_ops; mips_svr4_so_ops.in_dynsym_resolve_code = mips_linux_in_dynsym_resolve_code; } diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c index cde4f2e..e50a83a 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -1704,7 +1704,7 @@ ppc_linux_init_abi (struct gdbarch_info info, if (powerpc_so_ops.in_dynsym_resolve_code == NULL) { - powerpc_so_ops = svr4_so_ops; + powerpc_so_ops = linux_so_ops; /* Override dynamic resolve function. */ powerpc_so_ops.in_dynsym_resolve_code = powerpc_linux_in_dynsym_resolve_code; diff --git a/gdb/solib-spu.c b/gdb/solib-spu.c index fa2977e..6952f32 100644 --- a/gdb/solib-spu.c +++ b/gdb/solib-spu.c @@ -34,7 +34,7 @@ #include "breakpoint.h" #include "gdbthread.h" #include "gdb_bfd.h" - +#include "linux-tdep.h" #include "spu-tdep.h" /* Highest SPE id (file handle) the inferior may have. */ @@ -164,7 +164,7 @@ spu_current_sos (void) int i, size; /* First, retrieve the SVR4 shared library list. */ - head = svr4_so_ops.current_sos (); + head = linux_so_ops.current_sos (); /* Append our libraries to the end of the list. */ for (link_ptr = &head; *link_ptr; link_ptr = &(*link_ptr)->next) @@ -238,7 +238,7 @@ static void spu_free_so (struct so_list *so) { if (so->so_original_name[0] != '@') - svr4_so_ops.free_so (so); + linux_so_ops.free_so (so); } /* Relocate section addresses. */ @@ -247,7 +247,7 @@ spu_relocate_section_addresses (struct so_list *so, struct target_section *sec) { if (so->so_original_name[0] != '@') - svr4_so_ops.relocate_section_addresses (so, sec); + linux_so_ops.relocate_section_addresses (so, sec); else { unsigned long long addr; @@ -355,7 +355,7 @@ spu_bfd_open (char *pathname) /* Handle regular SVR4 libraries. */ if (!original_name) - return svr4_so_ops.bfd_open (pathname); + return linux_so_ops.bfd_open (pathname); /* Decode object ID. */ if (sscanf (original_name, "@0x%llx <%d>", &addr, &fd) != 2) @@ -399,8 +399,8 @@ spu_lookup_lib_symbol (struct objfile *objfile, if (bfd_get_arch (objfile->obfd) == bfd_arch_spu) return lookup_global_symbol_from_objfile (objfile, name, domain); - if (svr4_so_ops.lookup_lib_global_symbol != NULL) - return svr4_so_ops.lookup_lib_global_symbol (objfile, name, domain); + if (linux_so_ops.lookup_lib_global_symbol != NULL) + return linux_so_ops.lookup_lib_global_symbol (objfile, name, domain); return (struct block_symbol) {NULL, NULL}; } @@ -495,7 +495,7 @@ spu_solib_create_inferior_hook (int from_tty) } /* Call SVR4 hook -- this will re-insert the SVR4 solib breakpoints. */ - svr4_so_ops.solib_create_inferior_hook (from_tty); + linux_so_ops.solib_create_inferior_hook (from_tty); /* If the inferior is statically linked against libspe, we need to install our own solib breakpoint right now. Otherwise, it will be installed by @@ -514,7 +514,7 @@ set_spu_solib_ops (struct gdbarch *gdbarch) dependency on solib-svr4.c's _initialize routine. */ if (spu_so_ops.current_sos == NULL) { - spu_so_ops = svr4_so_ops; + spu_so_ops = linux_so_ops; spu_so_ops.solib_create_inferior_hook = spu_solib_create_inferior_hook; spu_so_ops.relocate_section_addresses = spu_relocate_section_addresses; spu_so_ops.free_so = spu_free_so; diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index fe36d45..d578683 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -51,27 +51,6 @@ static int svr4_have_link_map_offsets (void); static void svr4_relocate_main_executable (void); static void svr4_free_library_list (void *p_list); -/* Link map info to include in an allocated so_list entry. */ - -struct lm_info - { - /* Amount by which addresses in the binary should be relocated to - match the inferior. The direct inferior value is L_ADDR_INFERIOR. - When prelinking is involved and the prelink base address changes, - we may need a different offset - the recomputed offset is in L_ADDR. - It is commonly the same value. It is cached as we want to warn about - the difference and compute it only once. L_ADDR is valid - iff L_ADDR_P. */ - CORE_ADDR l_addr, l_addr_inferior; - unsigned int l_addr_p : 1; - - /* The target location of lm. */ - CORE_ADDR lm_addr; - - /* Values read in from inferior's fields of the same name. */ - CORE_ADDR l_ld, l_next, l_prev, l_name; - }; - /* On SVR4 systems, a list of symbols in the dynamic linker where GDB can try to place a breakpoint to monitor shared library events. @@ -189,12 +168,12 @@ svr4_same (struct so_list *gdb, struct so_list *inferior) return (svr4_same_1 (gdb->so_original_name, inferior->so_original_name)); } -static struct lm_info * +static struct svr4_lm_info * lm_info_read (CORE_ADDR lm_addr) { struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); gdb_byte *lm; - struct lm_info *lm_info; + struct svr4_lm_info *lm_info; struct cleanup *back_to; lm = (gdb_byte *) xmalloc (lmo->link_map_size); @@ -210,7 +189,7 @@ lm_info_read (CORE_ADDR lm_addr) { struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr; - lm_info = XCNEW (struct lm_info); + lm_info = XCNEW (struct svr4_lm_info); lm_info->lm_addr = lm_addr; lm_info->l_addr_inferior = extract_typed_address (&lm[lmo->l_addr_offset], @@ -237,20 +216,28 @@ has_lm_dynamic_from_link_map (void) return lmo->l_ld_offset >= 0; } +static struct svr4_lm_info * +svr4_lm_info (const struct so_list *so) +{ + return (struct svr4_lm_info *) so->lm_info; +} + static CORE_ADDR lm_addr_check (const struct so_list *so, bfd *abfd) { - if (!so->lm_info->l_addr_p) + struct svr4_lm_info *lm_info = svr4_lm_info (so); + + if (!lm_info->l_addr_p) { struct bfd_section *dyninfo_sect; CORE_ADDR l_addr, l_dynaddr, dynaddr; - l_addr = so->lm_info->l_addr_inferior; + l_addr = lm_info->l_addr_inferior; if (! abfd || ! has_lm_dynamic_from_link_map ()) goto set_addr; - l_dynaddr = so->lm_info->l_ld; + l_dynaddr = lm_info->l_ld; dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic"); if (dyninfo_sect == NULL) @@ -333,11 +320,11 @@ lm_addr_check (const struct so_list *so, bfd *abfd) } set_addr: - so->lm_info->l_addr = l_addr; - so->lm_info->l_addr_p = 1; + lm_info->l_addr = l_addr; + lm_info->l_addr_p = 1; } - return so->lm_info->l_addr; + return lm_info->l_addr; } /* Per pspace SVR4 specific data. */ @@ -978,6 +965,7 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) struct svr4_info *info; CORE_ADDR ldsomap; struct so_list *newobj; + struct svr4_lm_info *lm_info; struct cleanup *old_chain; CORE_ADDR name_lm; @@ -994,9 +982,10 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) newobj = XCNEW (struct so_list); old_chain = make_cleanup (xfree, newobj); - newobj->lm_info = lm_info_read (ldsomap); + lm_info = lm_info_read (ldsomap); + newobj->lm_info = (struct lm_info *) lm_info; make_cleanup (xfree, newobj->lm_info); - name_lm = newobj->lm_info ? newobj->lm_info->l_name : 0; + name_lm = lm_info ? lm_info->l_name : 0; do_cleanups (old_chain); return (name_lm >= vaddr && name_lm < vaddr + size); @@ -1102,8 +1091,10 @@ svr4_free_so (struct so_list *so) static void svr4_clear_so (struct so_list *so) { - if (so->lm_info != NULL) - so->lm_info->l_addr_p = 0; + struct svr4_lm_info *lm_info = svr4_lm_info (so); + + if (lm_info != NULL) + lm_info->l_addr_p = 0; } /* Free so_list built so far (called via cleanup). */ @@ -1133,12 +1124,14 @@ svr4_copy_library_list (struct so_list *src) while (src != NULL) { struct so_list *newobj; + struct svr4_lm_info *lm_info; newobj = XNEW (struct so_list); memcpy (newobj, src, sizeof (struct so_list)); - newobj->lm_info = XNEW (struct lm_info); - memcpy (newobj->lm_info, src->lm_info, sizeof (struct lm_info)); + lm_info = XNEW (struct svr4_lm_info); + newobj->lm_info = (struct lm_info *) lm_info; + memcpy (newobj->lm_info, src->lm_info, sizeof (struct svr4_lm_info)); newobj->next = NULL; *link = newobj; @@ -1172,12 +1165,14 @@ library_list_start_library (struct gdb_xml_parser *parser, ULONGEST *l_ldp = (ULONGEST *) xml_find_attribute (attributes, "l_ld")->value; struct so_list *new_elem; + struct svr4_lm_info *lm_info; new_elem = XCNEW (struct so_list); - new_elem->lm_info = XCNEW (struct lm_info); - new_elem->lm_info->lm_addr = *lmp; - new_elem->lm_info->l_addr_inferior = *l_addrp; - new_elem->lm_info->l_ld = *l_ldp; + lm_info = XCNEW (struct svr4_lm_info); + lm_info->lm_addr = *lmp; + lm_info->l_addr_inferior = *l_addrp; + lm_info->l_ld = *l_ldp; + new_elem->lm_info = (struct lm_info *) lm_info; strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1); new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0; @@ -1323,17 +1318,19 @@ svr4_default_sos (void) { struct svr4_info *info = get_svr4_info (); struct so_list *newobj; + struct svr4_lm_info *lm_info; if (!info->debug_loader_offset_p) return NULL; newobj = XCNEW (struct so_list); - newobj->lm_info = XCNEW (struct lm_info); + lm_info = XCNEW (struct svr4_lm_info); + newobj->lm_info = (struct lm_info *) lm_info; /* Nothing will ever check the other fields if we set l_addr_p. */ - newobj->lm_info->l_addr = info->debug_loader_offset; - newobj->lm_info->l_addr_p = 1; + lm_info->l_addr = info->debug_loader_offset; + lm_info->l_addr_p = 1; strncpy (newobj->so_name, info->debug_loader_name, SO_NAME_MAX_PATH_SIZE - 1); newobj->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; @@ -1363,24 +1360,26 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm, struct cleanup *old_chain; int errcode; char *buffer; + struct svr4_lm_info *lm_info; newobj = XCNEW (struct so_list); old_chain = make_cleanup_free_so (newobj); - newobj->lm_info = lm_info_read (lm); - if (newobj->lm_info == NULL) + lm_info = lm_info_read (lm); + if (lm_info == NULL) { do_cleanups (old_chain); return 0; } - next_lm = newobj->lm_info->l_next; + newobj->lm_info = (struct lm_info *) lm_info; + next_lm = lm_info->l_next; - if (newobj->lm_info->l_prev != prev_lm) + if (lm_info->l_prev != prev_lm) { warning (_("Corrupted shared library list: %s != %s"), paddress (target_gdbarch (), prev_lm), - paddress (target_gdbarch (), newobj->lm_info->l_prev)); + paddress (target_gdbarch (), lm_info->l_prev)); do_cleanups (old_chain); return 0; } @@ -1390,18 +1389,18 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm, SVR4, it has no name. For others (Solaris 2.3 for example), it does have a name, so we can no longer use a missing name to decide when to ignore it. */ - if (ignore_first && newobj->lm_info->l_prev == 0) + if (ignore_first && lm_info->l_prev == 0) { struct svr4_info *info = get_svr4_info (); - first_l_name = newobj->lm_info->l_name; - info->main_lm_addr = newobj->lm_info->lm_addr; + first_l_name = lm_info->l_name; + info->main_lm_addr = lm_info->lm_addr; do_cleanups (old_chain); continue; } /* Extract this shared object's name. */ - target_read_string (newobj->lm_info->l_name, &buffer, + target_read_string (lm_info->l_name, &buffer, SO_NAME_MAX_PATH_SIZE - 1, &errcode); if (errcode != 0) { @@ -1409,7 +1408,7 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm, inferior executable, then this is not a normal shared object, but (most likely) a vDSO. In this case, silently skip it; otherwise emit a warning. */ - if (first_l_name == 0 || newobj->lm_info->l_name != first_l_name) + if (first_l_name == 0 || lm_info->l_name != first_l_name) warning (_("Can't read pathname for load map: %s."), safe_strerror (errcode)); do_cleanups (old_chain); @@ -1515,7 +1514,7 @@ svr4_current_sos_direct (struct svr4_info *info) method. */ static struct so_list * -svr4_current_sos_1 (void) +svr4_current_sos (void) { struct svr4_info *info = get_svr4_info (); @@ -1528,82 +1527,6 @@ svr4_current_sos_1 (void) return svr4_current_sos_direct (info); } -/* Implement the "current_sos" target_so_ops method. */ - -static struct so_list * -svr4_current_sos (void) -{ - struct so_list *so_head = svr4_current_sos_1 (); - struct mem_range vsyscall_range; - - /* Filter out the vDSO module, if present. Its symbol file would - not be found on disk. The vDSO/vsyscall's OBJFILE is instead - managed by symfile-mem.c:add_vsyscall_page. */ - if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range) - && vsyscall_range.length != 0) - { - struct so_list **sop; - - sop = &so_head; - while (*sop != NULL) - { - struct so_list *so = *sop; - - /* We can't simply match the vDSO by starting address alone, - because lm_info->l_addr_inferior (and also l_addr) do not - necessarily represent the real starting address of the - ELF if the vDSO's ELF itself is "prelinked". The l_ld - field (the ".dynamic" section of the shared object) - always points at the absolute/resolved address though. - So check whether that address is inside the vDSO's - mapping instead. - - E.g., on Linux 3.16 (x86_64) the vDSO is a regular - 0-based ELF, and we see: - - (gdb) info auxv - 33 AT_SYSINFO_EHDR System-supplied DSO's ELF header 0x7ffff7ffb000 - (gdb) p/x *_r_debug.r_map.l_next - $1 = {l_addr = 0x7ffff7ffb000, ..., l_ld = 0x7ffff7ffb318, ...} - - And on Linux 2.6.32 (x86_64) we see: - - (gdb) info auxv - 33 AT_SYSINFO_EHDR System-supplied DSO's ELF header 0x7ffff7ffe000 - (gdb) p/x *_r_debug.r_map.l_next - $5 = {l_addr = 0x7ffff88fe000, ..., l_ld = 0x7ffff7ffe580, ... } - - Dumping that vDSO shows: - - (gdb) info proc mappings - 0x7ffff7ffe000 0x7ffff7fff000 0x1000 0 [vdso] - (gdb) dump memory vdso.bin 0x7ffff7ffe000 0x7ffff7fff000 - # readelf -Wa vdso.bin - [...] - Entry point address: 0xffffffffff700700 - [...] - Section Headers: - [Nr] Name Type Address Off Size - [ 0] NULL 0000000000000000 000000 000000 - [ 1] .hash HASH ffffffffff700120 000120 000038 - [ 2] .dynsym DYNSYM ffffffffff700158 000158 0000d8 - [...] - [ 9] .dynamic DYNAMIC ffffffffff700580 000580 0000f0 - */ - if (address_in_mem_range (so->lm_info->l_ld, &vsyscall_range)) - { - *sop = so->next; - free_so (so); - break; - } - - sop = &so->next; - } - } - - return so_head; -} - /* Get the address of the link_map for a given OBJFILE. */ CORE_ADDR @@ -1624,7 +1547,7 @@ svr4_fetch_objfile_link_map (struct objfile *objfile) of shared libraries. */ for (so = master_so_list (); so; so = so->next) if (so->objfile == objfile) - return so->lm_info->lm_addr; + return svr4_lm_info (so)->lm_addr; /* Not found! */ return 0; @@ -1856,7 +1779,7 @@ solist_update_incremental (struct svr4_info *info, CORE_ADDR lm) /* Walk to the end of the list. */ for (tail = info->solib_list; tail->next != NULL; tail = tail->next) /* Nothing. */; - prev_lm = tail->lm_info->lm_addr; + prev_lm = svr4_lm_info (tail)->lm_addr; /* Read the new objects. */ if (info->using_xfer) @@ -3187,7 +3110,8 @@ set_solib_svr4_fetch_link_map_offsets (struct gdbarch *gdbarch, ops->fetch_link_map_offsets = flmo; - set_solib_ops (gdbarch, &svr4_so_ops); + if (solib_ops (gdbarch) == NULL) + set_solib_ops (gdbarch, &svr4_so_ops); } /* Fetch a link_map_offsets structure using the architecture-specific diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h index d541136..87650d1 100644 --- a/gdb/solib-svr4.h +++ b/gdb/solib-svr4.h @@ -63,6 +63,27 @@ struct link_map_offsets int l_name_offset; }; +/* Link map info to include in an allocated so_list entry. */ + +struct svr4_lm_info + { + /* Amount by which addresses in the binary should be relocated to + match the inferior. The direct inferior value is L_ADDR_INFERIOR. + When prelinking is involved and the prelink base address changes, + we may need a different offset - the recomputed offset is in L_ADDR. + It is commonly the same value. It is cached as we want to warn about + the difference and compute it only once. L_ADDR is valid + iff L_ADDR_P. */ + CORE_ADDR l_addr, l_addr_inferior; + unsigned int l_addr_p : 1; + + /* The target location of lm. */ + CORE_ADDR lm_addr; + + /* Values read in from inferior's fields of the same name. */ + CORE_ADDR l_ld, l_next, l_prev, l_name; + }; + /* set_solib_svr4_fetch_link_map_offsets() is intended to be called by a _gdbarch_init() function. It is used to establish an architecture specific link_map_offsets fetcher for the architecture diff --git a/gdb/solib.c b/gdb/solib.c index 2235505..84ed485 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -62,7 +62,7 @@ solib_init (struct obstack *obstack) return ops; } -static const struct target_so_ops * +const struct target_so_ops * solib_ops (struct gdbarch *gdbarch) { const struct target_so_ops **ops diff --git a/gdb/solib.h b/gdb/solib.h index 00fd6cb..d594ea2 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -68,6 +68,10 @@ extern int in_solib_dynsym_resolve_code (CORE_ADDR); extern void no_shared_libraries (char *ignored, int from_tty); +/* Get the solib operations for GDBARCH. */ + +const struct target_so_ops *solib_ops (struct gdbarch *gdbarch); + /* Set the solib operations for GDBARCH to NEW_OPS. */ extern void set_solib_ops (struct gdbarch *gdbarch, diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c index 79739a6..00d20a2 100644 --- a/gdb/symfile-mem.c +++ b/gdb/symfile-mem.c @@ -82,7 +82,7 @@ target_read_memory_bfd (bfd_vma memaddr, bfd_byte *myaddr, bfd_size_type len) which will be attached to the BFD. */ static struct objfile * symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, - size_t size, char *name, int from_tty) + size_t size, char *name, int add_flags) { struct objfile *objf; struct bfd *nbfd; @@ -127,8 +127,7 @@ symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, sai->num_sections = i; objf = symbol_file_add_from_bfd (nbfd, bfd_get_filename (nbfd), - from_tty ? SYMFILE_VERBOSE : 0, - sai, OBJF_SHARED, NULL); + add_flags, sai, OBJF_SHARED, NULL); add_target_sections_of_objfile (objf); @@ -145,6 +144,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty) { CORE_ADDR addr; struct bfd *templ; + int add_flags = from_tty ? SYMFILE_VERBOSE : 0; if (args == NULL) error (_("add-symbol-file-from-memory requires an expression argument")); @@ -160,7 +160,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty) error (_("Must use symbol-file or exec-file " "before add-symbol-file-from-memory.")); - symbol_file_add_from_memory (templ, addr, 0, NULL, from_tty); + symbol_file_add_from_memory (templ, addr, 0, NULL, add_flags); } /* Arguments for symbol_file_add_from_memory_wrapper. */ @@ -171,7 +171,7 @@ struct symbol_file_add_from_memory_args CORE_ADDR sysinfo_ehdr; size_t size; char *name; - int from_tty; + int add_flags; }; /* Wrapper function for symbol_file_add_from_memory, for @@ -184,19 +184,15 @@ symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data) = (struct symbol_file_add_from_memory_args *) data; symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->size, - args->name, args->from_tty); + args->name, args->add_flags); return 0; } -/* Try to add the symbols for the vsyscall page, if there is one. - This function is called via the inferior_created observer. */ +/* See symfile-mem.h. */ -static void -add_vsyscall_page (struct target_ops *target, int from_tty) +void +add_vsyscall_page (struct mem_range *vsyscall_range, int add_flags) { - struct mem_range vsyscall_range; - - if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range)) { struct bfd *bfd; struct symbol_file_add_from_memory_args args; @@ -218,15 +214,13 @@ add_vsyscall_page (struct target_ops *target, int from_tty) return; } args.bfd = bfd; - args.sysinfo_ehdr = vsyscall_range.start; - args.size = vsyscall_range.length; + args.sysinfo_ehdr = vsyscall_range->start; + args.size = vsyscall_range->length; args.name = xstrprintf ("system-supplied DSO at %s", - paddress (target_gdbarch (), vsyscall_range.start)); - /* Pass zero for FROM_TTY, because the action of loading the - vsyscall DSO was not triggered by the user, even if the user - typed "run" at the TTY. */ - args.from_tty = 0; + paddress (target_gdbarch (), + vsyscall_range->start)); + args.add_flags = add_flags; catch_exceptions (current_uiout, symbol_file_add_from_memory_wrapper, &args, RETURN_MASK_ALL); } @@ -247,8 +241,4 @@ _initialize_symfile_mem (void) "Give an expression for the address " "of the file's shared object file header."), &cmdlist); - - /* Want to know of each new inferior so that its vsyscall info can - be extracted. */ - observer_attach_inferior_created (add_vsyscall_page); } diff --git a/gdb/symfile-mem.h b/gdb/symfile-mem.h new file mode 100644 index 0000000..df0e879 --- /dev/null +++ b/gdb/symfile-mem.h @@ -0,0 +1,30 @@ +/* Definitions for reading symbol files from memory into GDB. + + Copyright (C) 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 . */ + +#if !defined (SYMFILE_MEM_H) +#define SYMFILE_MEM_H + +struct mem_range; + +/* Try to add the symbols for the vsyscall page, if there is one. */ + +extern void add_vsyscall_page (struct mem_range *vsyscall_range, + int add_flags); + +#endif /* !defined(SYMFILE_MEM_H) */ -- 2.5.5