From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5263 invoked by alias); 18 Oct 2011 21:32:33 -0000 Received: (qmail 5253 invoked by uid 22791); 18 Oct 2011 21:32:30 -0000 X-SWARE-Spam-Status: No, hits=-6.7 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_CP,TW_XS,TW_XZ X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 18 Oct 2011 21:32:02 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p9ILVwWl022401 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 18 Oct 2011 17:31:58 -0400 Received: from host1.jankratochvil.net (ovpn-116-16.ams2.redhat.com [10.36.116.16]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p9ILVuDZ021398 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 18 Oct 2011 17:31:57 -0400 Received: from host1.jankratochvil.net (localhost [127.0.0.1]) by host1.jankratochvil.net (8.14.4/8.14.4) with ESMTP id p9ILVtPY006936; Tue, 18 Oct 2011 23:31:55 +0200 Received: (from jkratoch@localhost) by host1.jankratochvil.net (8.14.4/8.14.4/Submit) id p9ILVs1w006935; Tue, 18 Oct 2011 23:31:54 +0200 Date: Tue, 18 Oct 2011 21:53:00 -0000 From: Jan Kratochvil To: Eli Zaretskii Cc: gdb-patches@sourceware.org, drow@false.org, ppluzhnikov@google.com Subject: Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #3 Message-ID: <20111018213154.GA6807@host1.jankratochvil.net> References: <20111018205220.GC31533@host1.jankratochvil.net> <83fwiqui3p.fsf@gnu.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <83fwiqui3p.fsf@gnu.org> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-10/txt/msg00515.txt.bz2 On Tue, 18 Oct 2011 23:04:26 +0200, Eli Zaretskii wrote: > Can we reword this not to say "For SVR4 systems" twice in a row? done. Thanks, Jan gdb/doc/ 2011-10-18 Jan Kratochvil * gdb.texinfo (Library List Format): Add SVR4 format description. gdb/gdbserver/ 2011-10-18 Jan Kratochvil Paul Pluzhnikov * inferiors.c (main_lm): New variable. (loaded_dll): Rename to ... (loaded_svr4_so): ... here, add parameters lm_addr and l_ld, fill them in. (loaded_dll, clear_all_dlls): New function. (clear_inferiors): Move some code to clear_all_dlls, call it. * linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug) (struct map_entry, map_entries, map_entries_count, map_entries_free) (map_entries_init, map_entries_get_compar, map_entries_get_start) (read_one_ptr, struct link_map_offsets, linux_refresh_libraries): New. (struct linux_target_ops): Install linux_qxfer_libraries. * linux-low.h (struct process_info_private): New member r_debug. * server.c (handle_qxfer_libraries): Call the_target->qxfer_libraries. Fill in optionally also main_lm and for each library lm_addr and l_ld. * server.h (struct dll_info): New fields lm_addr and l_ld. (main_lm, clear_all_dlls, loaded_svr4_so): New declarations. * target.h (struct target_ops): New member qxfer_libraries. gdb/ 2011-10-18 Jan Kratochvil Paul Pluzhnikov * features/library-list.dtd: Add attributes library-list.main-lm, library.lm and library.l_ld. * solib-svr4.c: Include solib-target.h. (struct lm_info): New field l_addr_inferior_is_absolute. (svr4_file_displacement): New function. (lm_addr_check): Use it if L_ADDR_INFERIOR_IS_ABSOLUTE. Extend the corruption message by addresses. (svr4_from_solib_target): New function. (svr4_current_sos): New variable solib_target_list, call solib_target_list_read and svr4_from_solib_target appropriately. * solib-target.c (library_list_start_library): New variables lm and l_ld, fill in the lib fields with them. (library_list_start_list): New variable list. New variable main_lm, fill in the lib field with it. (library_attributes): New entries lm and l_ld. (library_list_attributes): New entry main-lm. * solib-target.h (struct solib_target_lib): New fields lm_addr and l_ld. (struct solib_target_list): New field main_lm. gdb/testsuite/ 2011-10-18 Jan Kratochvil * gdb.base/solib-corrupted.exp: Suppress test on is_remote target. (corrupted list): Adjust the expectation. --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -36989,6 +36989,19 @@ should instead include a list of allocated sections. The segment or section bases are start addresses, not relocation offsets; they do not depend on the library's link-time base addresses. +For SVR4 systems, a single segment is reported and its parent +@code{library-list} element should have @code{main-lm} attribute with address +of @code{struct link_map} used for the main executable. Additionally for each +SVR4 @code{library} element the following parameters are reported: + +@itemize @minus +@item +@code{lm} with address of @code{struct link_map} used for TLS (Thread Local +Storage) access +@item +@code{l_ld}, which is memory address of the @code{PT_DYNAMIC} segment +@end itemize + @value{GDBN} must be linked with the Expat library to support XML library lists. @xref{Expat}. @@ -37016,14 +37029,30 @@ allocated sections (.text, .data, .bss), looks like this: @end smallexample +SVR4 systems report: + +@smallexample + + + + + + + + +@end smallexample + The format of a library list is described by this DTD: @smallexample + + + @@ -37032,7 +37061,9 @@ The format of a library list is described by this DTD: In addition, segments and section descriptors cannot be mixed within a single library element, and you must supply at least one segment or -section for each library. +section for each library. For SVR4 systems each library must have one +segment descriptor and both the @code{lm} and @code{l_ld} attributes +must be present for each library; the @code{main-lm} attribute is optional. @node Memory Map Format @section Memory Map Format --- a/gdb/features/library-list.dtd +++ b/gdb/features/library-list.dtd @@ -7,9 +7,12 @@ + + + --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -28,6 +28,10 @@ struct inferior_list all_threads; struct inferior_list all_dlls; int dlls_changed; +/* Address of main executable's struct link_map for the current inferior. + It may be 0 if it is not known. */ +CORE_ADDR main_lm; + struct thread_info *current_inferior; #define get_thread(inf) ((struct thread_info *)(inf)) @@ -258,10 +262,13 @@ match_dll (struct inferior_list_entry *inf, void *arg) return 0; } -/* Record a newly loaded DLL at BASE_ADDR. */ +/* Record a newly loaded DLL at BASE_ADDR, struct link_map at LM_ADDR and + PT_DYNAMIC at L_LD. LM_ADDR and L_LD can be 0 if not known. BASE_ADDR is + not link_map->l_addr as l_addr is only bias, not the address. */ void -loaded_dll (const char *name, CORE_ADDR base_addr) +loaded_svr4_so (const char *name, CORE_ADDR base_addr, CORE_ADDR lm_addr, + CORE_ADDR l_ld) { struct dll_info *new_dll = xmalloc (sizeof (*new_dll)); memset (new_dll, 0, sizeof (*new_dll)); @@ -270,11 +277,21 @@ loaded_dll (const char *name, CORE_ADDR base_addr) new_dll->name = xstrdup (name); new_dll->base_addr = base_addr; + new_dll->lm_addr = lm_addr; + new_dll->l_ld = l_ld; add_inferior_to_list (&all_dlls, &new_dll->entry); dlls_changed = 1; } +/* Record a newly loaded DLL at BASE_ADDR. */ + +void +loaded_dll (const char *name, CORE_ADDR base_addr) +{ + loaded_svr4_so (name, base_addr, 0, 0); +} + /* Record that the DLL with NAME and BASE_ADDR has been unloaded. */ void @@ -312,14 +329,23 @@ unloaded_dll (const char *name, CORE_ADDR base_addr) #define clear_list(LIST) \ do { (LIST)->head = (LIST)->tail = NULL; } while (0) +/* Clear ALL_DLLS and MAIN_LM. */ + void -clear_inferiors (void) +clear_all_dlls (void) { - for_each_inferior (&all_threads, free_one_thread); for_each_inferior (&all_dlls, free_one_dll); + clear_list (&all_dlls); + main_lm = 0; +} +void +clear_inferiors (void) +{ + for_each_inferior (&all_threads, free_one_thread); clear_list (&all_threads); - clear_list (&all_dlls); + + clear_all_dlls (); current_inferior = NULL; } --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -4953,6 +4953,444 @@ linux_emit_ops (void) return NULL; } +/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */ + +static int +get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64, + CORE_ADDR *phdr_memaddr, int *num_phdr) +{ + char filename[PATH_MAX]; + int fd; + const int auxv_size = is_elf64 + ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t); + char buf[sizeof (Elf64_auxv_t)]; /* The larger of the two. */ + + xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid); + + fd = open (filename, O_RDONLY); + if (fd < 0) + return 1; + + *phdr_memaddr = 0; + *num_phdr = 0; + while (read (fd, buf, auxv_size) == auxv_size + && (*phdr_memaddr == 0 || *num_phdr == 0)) + { + if (is_elf64) + { + Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf; + + switch (aux->a_type) + { + case AT_PHDR: + *phdr_memaddr = aux->a_un.a_val; + break; + case AT_PHNUM: + *num_phdr = aux->a_un.a_val; + break; + } + } + else + { + Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf; + + switch (aux->a_type) + { + case AT_PHDR: + *phdr_memaddr = aux->a_un.a_val; + break; + case AT_PHNUM: + *num_phdr = aux->a_un.a_val; + break; + } + } + } + + close (fd); + + if (*phdr_memaddr == 0 || *num_phdr == 0) + { + warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: " + "phdr_memaddr = %ld, phdr_num = %d", + (long) *phdr_memaddr, *num_phdr); + return 2; + } + + return 0; +} + +/* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */ + +static CORE_ADDR +get_dynamic (const int pid, const int is_elf64) +{ + CORE_ADDR phdr_memaddr, relocation; + int num_phdr, i; + unsigned char *phdr_buf; + const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr); + + if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr)) + return 0; + + gdb_assert (num_phdr < 100); /* Basic sanity check. */ + phdr_buf = alloca (num_phdr * phdr_size); + + if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size)) + return 0; + + /* Compute relocation: it is expected to be 0 for "regular" executables, + non-zero for PIE ones. */ + relocation = -1; + for (i = 0; relocation == -1 && i < num_phdr; i++) + if (is_elf64) + { + Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_PHDR) + relocation = phdr_memaddr - p->p_vaddr; + } + else + { + Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_PHDR) + relocation = phdr_memaddr - p->p_vaddr; + } + + if (relocation == -1) + { + warning ("Unexpected missing PT_PHDR"); + return 0; + } + + for (i = 0; i < num_phdr; i++) + { + if (is_elf64) + { + Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_DYNAMIC) + return p->p_vaddr + relocation; + } + else + { + Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_DYNAMIC) + return p->p_vaddr + relocation; + } + } + + return 0; +} + +/* Return &_r_debug in the inferior, or -1 if not present. Return value + can be 0 if the inferior does not yet have the library list initialized. */ + +static CORE_ADDR +get_r_debug (const int pid, const int is_elf64) +{ + CORE_ADDR dynamic_memaddr; + const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn); + unsigned char buf[sizeof (Elf64_Dyn)]; /* The larger of the two. */ + + dynamic_memaddr = get_dynamic (pid, is_elf64); + if (dynamic_memaddr == 0) + return (CORE_ADDR) -1; + + while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0) + { + if (is_elf64) + { + Elf64_Dyn *const dyn = (Elf64_Dyn *) buf; + + if (dyn->d_tag == DT_DEBUG) + return dyn->d_un.d_val; + + if (dyn->d_tag == DT_NULL) + break; + } + else + { + Elf32_Dyn *const dyn = (Elf32_Dyn *) buf; + + if (dyn->d_tag == DT_DEBUG) + return dyn->d_un.d_val; + + if (dyn->d_tag == DT_NULL) + break; + } + + dynamic_memaddr += dyn_size; + } + + return (CORE_ADDR) -1; +} + +/* Parsed line from /proc/PID/maps - the 1st, 2nd and 4th column. */ +struct map_entry +{ + CORE_ADDR start, end; + + /* File offset where START is mapped from. */ + CORE_ADDR offset; +}; + +/* Cached array of MAP_ENTRIES_COUNT entries from /proc/PID/maps. */ +static struct map_entry *map_entries; +static unsigned map_entries_count; + +/* Free map_entries and map_entries_count. */ + +static void +map_entries_free (void) +{ + free (map_entries); + map_entries = NULL; + map_entries_count = 0; +} + +/* Load MAP_ENTRIES and MAP_ENTRIES_COUNT from /proc/PID/maps. Keep them clear + if /proc/PID/maps cannot be read. */ + +static void +map_entries_init (pid_t pid) +{ + unsigned allocated = 0; + char fname[50]; + FILE *f; + unsigned long start, end, offset; + int line = 1; + + map_entries_free (); + xsnprintf (fname, sizeof (fname), "/proc/%d/maps", (int) pid); + f = fopen (fname, "r"); + if (f == NULL) + { + warning ("Opening \"%s\": %s", fname, strerror (errno)); + return; + } + while (fscanf (f, "%lx-%lx%*s%lx", &start, &end, &offset) == 3) + { + if (map_entries_count && map_entries[map_entries_count - 1].end > start) + warning ("Error reading \"%s\" line %d: start < previous end", fname, + line); + else if (start > end) + warning ("Error reading \"%s\" line %d: start > end", fname, line); + else + { + struct map_entry *entry; + + if (map_entries_count == allocated) + { + allocated = allocated ? allocated * 2 : 0x40; + map_entries = xrealloc (map_entries, + allocated * sizeof (*map_entries)); + } + entry = &map_entries[map_entries_count++]; + entry->start = start; + entry->end = end; + entry->offset = offset; + } + while (!ferror (f) && !feof (f) && fgetc (f) != '\n'); + if (ferror (f) || feof (f)) + break; + line++; + } + if (ferror (f)) + warning ("Error reading \"%s\" line %d: %s", fname, line, strerror (errno)); + if (fclose (f)) + warning ("Error closing \"%s\": %s", fname, strerror (errno)); +} + +/* Find address stored in KEY, bsearch helper for map_entries_get_start. */ + +static int +map_entries_get_compar (const void *key, const void *entry_voidp) +{ + CORE_ADDR l_ld = *(const CORE_ADDR *) key; + const struct map_entry *entry = entry_voidp; + + if (l_ld < entry->start) + return -1; + else if (l_ld < entry->end) + return 0; + else + return +1; +} + +/* Find address where start of a file is mapped to if L_LD is located somewhere + in the file. Return zero otherwise. */ + +static CORE_ADDR +map_entries_get_start (CORE_ADDR l_ld) +{ + struct map_entry *foundp; + + foundp = bsearch (&l_ld, map_entries, map_entries_count, + sizeof (*map_entries), map_entries_get_compar); + + return foundp ? foundp->start - foundp->offset : 0; +} + +/* Read one pointer from MEMADDR in the inferior. */ + +static int +read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size) +{ + *ptr = 0; + return linux_read_memory (memaddr, (unsigned char *) ptr, ptr_size); +} + +struct link_map_offsets + { + /* Offset and size of r_debug.r_version. */ + int r_version_offset; + + /* Offset and size of r_debug.r_map. */ + int r_map_offset; + + /* Offset to l_addr field in struct link_map. */ + int l_addr_offset; + + /* Offset to l_name field in struct link_map. */ + int l_name_offset; + + /* Offset to l_ld field in struct link_map. */ + int l_ld_offset; + + /* Offset to l_next field in struct link_map. */ + int l_next_offset; + + /* Offset to l_prev field in struct link_map. */ + int l_prev_offset; + }; + +/* Construct qXfer:libraries:read reply. */ + +static int +linux_refresh_libraries (void) +{ + struct process_info_private *const priv = current_process ()->private; + char filename[PATH_MAX]; + int pid, is_elf64, ptr_size, r_version; + CORE_ADDR lm_addr, lm_prev, l_addr, l_name, l_ld, l_next, l_prev; + + static const struct link_map_offsets lmo_32bit_offsets = + { + 0, /* r_version offset. */ + 4, /* r_debug.r_map offset. */ + 0, /* l_addr offset in link_map. */ + 4, /* l_name offset in link_map. */ + 8, /* l_ld offset in link_map. */ + 12, /* l_next offset in link_map. */ + 16 /* l_prev offset in link_map. */ + }; + + static const struct link_map_offsets lmo_64bit_offsets = + { + 0, /* r_version offset. */ + 8, /* r_debug.r_map offset. */ + 0, /* l_addr offset in link_map. */ + 8, /* l_name offset in link_map. */ + 16, /* l_ld offset in link_map. */ + 24, /* l_next offset in link_map. */ + 32 /* l_prev offset in link_map. */ + }; + const struct link_map_offsets *lmo; + + pid = lwpid_of (get_thread_lwp (current_inferior)); + xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid); + is_elf64 = elf_64_file_p (filename); + lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets; + ptr_size = is_elf64 ? 8 : 4; + + clear_all_dlls (); + + if (priv->r_debug == 0) + priv->r_debug = get_r_debug (pid, is_elf64); + if (priv->r_debug == (CORE_ADDR) -1) + return 0; + if (priv->r_debug == 0) + return 1; + + r_version = 0; + if (linux_read_memory (priv->r_debug + lmo->r_version_offset, + (unsigned char *) &r_version, + sizeof (r_version)) != 0 + || r_version != 1) + { + warning ("unexpected r_debug version %d", r_version); + return 0; + } + + if (read_one_ptr (priv->r_debug + lmo->r_map_offset, + &lm_addr, ptr_size) != 0) + { + warning ("unable to read r_map from 0x%lx", + (long) priv->r_debug + lmo->r_map_offset); + return 0; + } + + map_entries_init (pid); + + lm_prev = 0; + while (read_one_ptr (lm_addr + lmo->l_name_offset, + &l_name, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_addr_offset, + &l_addr, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_ld_offset, + &l_ld, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_prev_offset, + &l_prev, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_next_offset, + &l_next, ptr_size) == 0) + { + unsigned char libname[PATH_MAX]; + + if (lm_prev != l_prev) + { + warning ("Corrupted shared library list: 0x%lx != 0x%lx", + (long) lm_prev, (long) l_prev); + break; + } + + /* Not checking for error because reading may stop before + we've got PATH_MAX worth of characters. */ + libname[0] = '\0'; + linux_read_memory (l_name, libname, sizeof (libname) - 1); + libname[sizeof (libname) - 1] = '\0'; + + if (libname[0] != '\0') + { + CORE_ADDR start; + + start = map_entries_get_start (l_ld); + if (start == 0) + { + /* Use at least L_ADDR if we failed to read /proc. But L_ADDR is + wrong for this purpose if the library file is prelinked. */ + start = l_addr; + } + + loaded_svr4_so ((const char *) libname, start, lm_addr, l_ld); + } + else if (lm_prev == 0) + main_lm = lm_addr; + + if (l_next == 0) + break; + + lm_prev = lm_addr; + lm_addr = l_next; + } + map_entries_free (); + + /* The library notification response is not expected for GNU/Linux. */ + dlls_changed = 0; + return 1; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -5014,6 +5452,7 @@ static struct target_ops linux_target_ops = { linux_install_fast_tracepoint_jump_pad, linux_emit_ops, linux_supports_disable_randomization, + linux_refresh_libraries }; static void --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -56,6 +56,9 @@ struct process_info_private /* libthread_db-specific additions. Not NULL if this process has loaded thread_db, and it is active. */ struct thread_db *thread_db; + + /* &_r_debug. 0 if not yet determined. -1 if no PT_DYNAMIC in Phdrs. */ + CORE_ADDR r_debug; }; struct lwp_info; --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -942,6 +942,10 @@ handle_qxfer_libraries (const char *annex, if (annex[0] != '\0' || !target_running ()) return -1; + if (the_target->refresh_libraries != NULL + && !the_target->refresh_libraries ()) + return -1; + /* Over-estimate the necessary memory. Assume that every character in the library name must be escaped. */ total_len = 64; @@ -952,19 +956,30 @@ handle_qxfer_libraries (const char *annex, if (document == NULL) return -1; - strcpy (document, "\n"); + strcpy (document, "\n"); + for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next) { struct dll_info *dll = (struct dll_info *) dll_ptr; char *name; name = xml_escape_text (dll->name); - p += sprintf (p, " " - "\n", - name, (unsigned long) dll->base_addr); + p += sprintf (p, " lm_addr) + p += sprintf (p, " lm=\"0x%lx\"", (unsigned long) dll->lm_addr); + if (dll->l_ld) + p += sprintf (p, " l_ld=\"0x%lx\"", (unsigned long) dll->l_ld); + + p += sprintf (p, ">\n", + (unsigned long) dll->base_addr); } strcpy (p, "\n"); --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -183,6 +183,10 @@ struct dll_info struct inferior_list_entry entry; char *name; CORE_ADDR base_addr; + + /* struct link_map address and the PT_DYNAMIC segment address for SVR4 + systems, if known. Otherwise these fields are zero. */ + CORE_ADDR lm_addr, l_ld; }; struct sym_cache; @@ -235,6 +239,7 @@ void initialize_low (); extern struct inferior_list all_processes; extern struct inferior_list all_threads; extern struct inferior_list all_dlls; +extern CORE_ADDR main_lm; extern int dlls_changed; void add_inferior_to_list (struct inferior_list *list, @@ -260,6 +265,7 @@ ptid_t thread_id_to_gdb_id (ptid_t); ptid_t thread_to_gdb_id (struct thread_info *); ptid_t gdb_id_to_thread_id (ptid_t); struct thread_info *gdb_id_to_thread (unsigned int); +void clear_all_dlls (void); void clear_inferiors (void); struct inferior_list_entry *find_inferior (struct inferior_list *, @@ -275,6 +281,8 @@ void set_inferior_regcache_data (struct thread_info *, void *); void add_pid_to_list (struct inferior_list *list, unsigned long pid); int pull_pid_from_list (struct inferior_list *list, unsigned long pid); +void loaded_svr4_so (const char *name, CORE_ADDR base_addr, CORE_ADDR lm_addr, + CORE_ADDR l_ld); void loaded_dll (const char *name, CORE_ADDR base_addr); void unloaded_dll (const char *name, CORE_ADDR base_addr); --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -380,6 +380,10 @@ struct target_ops /* Returns true if the target supports disabling randomization. */ int (*supports_disable_randomization) (void); + + /* Refresh ALL_DLLS. Return 1 if ALL_DLLS is valid, return 0 if some error + occured reading the inferior. */ + int (*refresh_libraries) (void); }; extern struct target_ops *the_target; --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -41,6 +41,7 @@ #include "solist.h" #include "solib.h" #include "solib-svr4.h" +#include "solib-target.h" #include "bfd-target.h" #include "elf-bfd.h" @@ -66,6 +67,11 @@ struct lm_info CORE_ADDR l_addr, l_addr_inferior; unsigned int l_addr_p : 1; + /* If this flag is zero L_ADDR_INFERIOR is the bias from inferior + link_map->l_addr. If this flag is set L_ADDR_INFERIOR is the absolute + address where the library mapping starts. */ + unsigned int l_addr_inferior_is_absolute : 1; + /* The target location of lm. */ CORE_ADDR lm_addr; @@ -189,6 +195,39 @@ has_lm_dynamic_from_link_map (void) return lmo->l_ld_offset >= 0; } +/* Find where ABFD file is prelinked to and return the address. Return + zero otherwise. */ + +static CORE_ADDR +svr4_file_displacement (bfd *abfd) +{ + long phdr_size; + Elf_Internal_Phdr *phdrs; + int num_phdrs = -1, i; + + phdr_size = bfd_get_elf_phdr_upper_bound (abfd); + if (phdr_size != -1) + { + phdrs = alloca (phdr_size); + + num_phdrs = bfd_get_elf_phdrs (abfd, phdrs); + } + if (num_phdrs == -1) + { + warning (_("Cannot find program headers in \"%s\": %s"), + bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ())); + return 0; + } + + for (i = 0; i < num_phdrs; i++) + if (phdrs[i].p_type == PT_PHDR) + return phdrs[i].p_vaddr - phdrs[i].p_offset; + + warning (_("Cannot find PT_PHDR in program headers of \"%s\""), + bfd_get_filename (abfd)); + return 0; +} + static CORE_ADDR lm_addr_check (struct so_list *so, bfd *abfd) { @@ -210,6 +249,9 @@ lm_addr_check (struct so_list *so, bfd *abfd) dynaddr = bfd_section_vma (abfd, dyninfo_sect); + if (so->lm_info->l_addr_inferior_is_absolute) + l_addr -= svr4_file_displacement (abfd); + if (dynaddr + l_addr != l_dynaddr) { CORE_ADDR align = 0x1000; @@ -1032,7 +1074,9 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, if (new->lm_info->l_prev != prev_lm) { - warning (_("Corrupted shared library list")); + warning (_("Corrupted shared library list: %s != %s"), + paddress (target_gdbarch, prev_lm), + paddress (target_gdbarch, new->lm_info->l_prev)); do_cleanups (old_chain); break; } @@ -1082,6 +1126,49 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr, } } +/* Convert LIST into the so_list format and return it. LIST lifetime is + handled by the caller. This function may throw an exception if LIST is not + appropriate for SVR4. */ + +static struct so_list * +svr4_from_solib_target (struct solib_target_list *list) +{ + struct solib_target_lib *lib; + int ix; + struct so_list *head = NULL, **tailp = &head; + struct cleanup *back_to = make_cleanup (svr4_free_library_list, &head); + + for (ix = 0; VEC_iterate (solib_target_libp, list->vec, ix, lib); ix++) + { + struct so_list *so; + + gdb_assert (lib->offsets == NULL); + if (!VEC_empty (CORE_ADDR, lib->section_bases)) + error (_("gdbserver libraries with sections are unsupported by SVR4")); + if (VEC_length (CORE_ADDR, lib->segment_bases) != 1) + error (_("gdbserver libraries with segment count not 1 are unsupported " + "by SVR4")); + + so = xzalloc (sizeof (*so)); + so->lm_info = xzalloc (sizeof (*so->lm_info)); + so->lm_info->lm_addr = lib->lm_addr; + so->lm_info->l_addr_inferior = VEC_index (CORE_ADDR, lib->segment_bases, + 0); + so->lm_info->l_addr_inferior_is_absolute = 1; + so->lm_info->l_ld = lib->l_ld; + + strncpy (so->so_name, lib->name, sizeof (so->so_name) - 1); + so->so_name[sizeof (so->so_name) - 1] = 0; + strcpy (so->so_original_name, so->so_name); + + *tailp = so; + tailp = &so->next; + } + + discard_cleanups (back_to); + return head; +} + /* Implement the "current_sos" target_so_ops method. */ static struct so_list * @@ -1093,6 +1180,32 @@ svr4_current_sos (void) struct svr4_info *info; struct cleanup *back_to; int ignore_first; + struct solib_target_list solib_target_list; + + /* Optionally support gdbserver XML library list. */ + if (solib_target_list_read (&solib_target_list)) + { + back_to = make_cleanup_solib_target_list_free (&solib_target_list); + + if (solib_target_list.main_lm) + { + info = get_svr4_info (); + info->main_lm_addr = solib_target_list.main_lm; + } + + if (VEC_empty (solib_target_libp, solib_target_list.vec)) + { + /* If the gdbserver packet succeeded and returned an empty list the + inferior DT_DEBUG is known but currently no libraries are loaded + in the inferior. */ + head = svr4_default_sos (); + } + else + head = svr4_from_solib_target (&solib_target_list); + + do_cleanups (back_to); + return head; + } info = get_svr4_info (); --- a/gdb/solib-target.c +++ b/gdb/solib-target.c @@ -107,8 +107,14 @@ library_list_start_library (struct gdb_xml_parser *parser, struct solib_target_list *list = user_data; struct solib_target_lib *lib = xzalloc (sizeof (*lib)); const char *name = xml_find_attribute (attributes, "name")->value; + struct gdb_xml_value *lm = xml_find_attribute (attributes, "lm"); + struct gdb_xml_value *l_ld = xml_find_attribute (attributes, "l_ld"); lib->name = xstrdup (name); + if (lm) + lib->lm_addr = *(ULONGEST *) lm->value; + if (l_ld) + lib->l_ld = *(ULONGEST *) l_ld->value; VEC_safe_push (solib_target_libp, list->vec, lib); } @@ -133,12 +139,16 @@ library_list_start_list (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, void *user_data, VEC(gdb_xml_value_s) *attributes) { + struct solib_target_list *list = user_data; char *version = xml_find_attribute (attributes, "version")->value; + struct gdb_xml_value *main_lm = xml_find_attribute (attributes, "main-lm"); if (strcmp (version, "1.0") != 0) gdb_xml_error (parser, _("Library list has unsupported version \"%s\""), version); + if (main_lm) + list->main_lm = *(ULONGEST *) main_lm->value; } /* Discard the constructed library list. Do not free its memory itself as it @@ -185,6 +195,8 @@ static const struct gdb_xml_element library_children[] = { static const struct gdb_xml_attribute library_attributes[] = { { "name", GDB_XML_AF_NONE, NULL, NULL }, + { "lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, + { "l_ld", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, { NULL, GDB_XML_AF_NONE, NULL, NULL } }; @@ -197,6 +209,7 @@ static const struct gdb_xml_element library_list_children[] = { static const struct gdb_xml_attribute library_list_attributes[] = { { "version", GDB_XML_AF_NONE, NULL, NULL }, + { "main-lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, { NULL, GDB_XML_AF_NONE, NULL, NULL } }; --- a/gdb/solib-target.h +++ b/gdb/solib-target.h @@ -32,6 +32,10 @@ struct solib_target_lib so_list; it is only here during XML parsing. */ char *name; + /* struct link_map address and the PT_DYNAMIC segment address for SVR4 + systems, if known. Otherwise these fields are zero. */ + CORE_ADDR lm_addr, l_ld; + /* The target can either specify segment bases or section bases, not both. */ @@ -56,6 +60,10 @@ struct solib_target_list { /* All libraries. The vector can be empty. */ VEC(solib_target_libp) *vec; + + /* struct link_map address of the main executable, if known. Otherwise this + field is zero. */ + CORE_ADDR main_lm; }; extern int solib_target_list_read (struct solib_target_list *list); --- a/gdb/testsuite/gdb.base/solib-corrupted.exp +++ b/gdb/testsuite/gdb.base/solib-corrupted.exp @@ -17,6 +17,12 @@ if {[skip_shlib_tests]} { return 0 } +if {[is_remote target]} { + # gdbserver prints the warning message but expect is parsing only the GDB + # output, not the gdbserver output. + return 0 +} + set testfile "solib-corrupted" set srcfile start.c @@ -47,4 +53,4 @@ gdb_test_multiple "p/x _r_debug->r_map->l_next = _r_debug->r_map" $test { pass $test } } -gdb_test "info sharedlibrary" "warning: Corrupted shared library list\r\n.*" "corrupted list" +gdb_test "info sharedlibrary" "warning: Corrupted shared library list: .*" "corrupted list"