diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2011-12-02 22:26:54 +0000 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2011-12-02 22:26:54 +0000 |
commit | 2268b414f486239cbcc0f756f157c3e03599efac (patch) | |
tree | b5837ff00eaa715115c6ab6d7425c274183c2c1e /gdb/gdbserver | |
parent | 1b31d05e6b9e7ee812949dc8c488f1f662ae6add (diff) | |
download | gdb-2268b414f486239cbcc0f756f157c3e03599efac.zip gdb-2268b414f486239cbcc0f756f157c3e03599efac.tar.gz gdb-2268b414f486239cbcc0f756f157c3e03599efac.tar.bz2 |
gdb/
* Makefile.in (XMLFILES): Add library-list-svr4.dtd.
* features/library-list-svr4.dtd: New file.
* remote.c (PACKET_qXfer_libraries_svr4): New.
(remote_protocol_features): new entry for PACKET_qXfer_libraries_svr4.
(remote_xfer_partial): Handle TARGET_OBJECT_LIBRARIES_SVR4.
* solib-svr4.c (struct svr4_library_list): New.
[HAVE_LIBEXPAT]: Include xml-support.h.
[HAVE_LIBEXPAT] (svr4_library_list_start_library)
[HAVE_LIBEXPAT] (svr4_library_list_start_list, svr4_library_attributes)
[HAVE_LIBEXPAT] (svr4_library_list_children)
[HAVE_LIBEXPAT] (svr4_library_list_attributes)
[HAVE_LIBEXPAT] (svr4_library_list_elements, svr4_parse_libraries)
[HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries)
[!HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries): New.
(svr4_read_so_list): Extend the corruption message by addresses.
(svr4_current_sos): New variable library_list, call
svr4_current_sos_via_xfer_libraries.
* target.h (enum target_object): New TARGET_OBJECT_LIBRARIES_SVR4.
gdb/gdbserver/
* linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
(read_one_ptr, struct link_map_offsets, linux_qxfer_libraries_svr4):
New.
(struct linux_target_ops): Install linux_qxfer_libraries_svr4.
* linux-low.h (struct process_info_private): New member r_debug.
* server.c (handle_qxfer_libraries): Call
the_target->qxfer_libraries_svr4.
(handle_qxfer_libraries_svr4): New function.
(qxfer_packets): New entry "libraries-svr4".
(handle_query): Check QXFER_LIBRARIES_SVR4 and report libraries-svr4.
* target.h (struct target_ops): New member qxfer_libraries_svr4.
* remote.c (remote_xfer_partial): Call add_packet_config_cmd for
PACKET_qXfer_libraries_svr4.
gdb/doc/
* gdb.texinfo (Requirements, Remote Protocol): Reference also `Library
List Format for SVR4 Targets'.
(General Query Packets): New item qXfer:libraries-svr4:read.
(Library List Format for SVR4 Targets): New node.
gdb/testsuite/
* gdb.base/solib-corrupted.exp: Suppress test on is_remote target.
(corrupted list): Adjust the expectation.
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r-- | gdb/gdbserver/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 377 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 3 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 33 | ||||
-rw-r--r-- | gdb/gdbserver/target.h | 5 |
5 files changed, 432 insertions, 3 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 464d9df..3982763 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,20 @@ +2011-12-02 Paul Pluzhnikov <ppluzhnikov@google.com> + Jan Kratochvil <jan.kratochvil@redhat.com> + + * linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug) + (read_one_ptr, struct link_map_offsets, linux_qxfer_libraries_svr4): + New. + (struct linux_target_ops): Install linux_qxfer_libraries_svr4. + * linux-low.h (struct process_info_private): New member r_debug. + * server.c (handle_qxfer_libraries): Call + the_target->qxfer_libraries_svr4. + (handle_qxfer_libraries_svr4): New function. + (qxfer_packets): New entry "libraries-svr4". + (handle_query): Check QXFER_LIBRARIES_SVR4 and report libraries-svr4. + * target.h (struct target_ops): New member qxfer_libraries_svr4. + * remote.c (remote_xfer_partial): Call add_packet_config_cmd for + PACKET_qXfer_libraries_svr4. + 2011-11-30 Ulrich Weigand <uweigand@de.ibm.com> * linux-s390-low.c (s390_collect_ptrace_register): Fully convert diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 2b973c6..539b63a 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -4964,6 +4964,382 @@ linux_get_min_fast_tracepoint_insn_len (void) return (*the_low_target.get_min_fast_tracepoint_insn_len) (); } +/* 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; +} + +/* 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_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, + unsigned const char *writebuf, + CORE_ADDR offset, int len) +{ + char *document; + unsigned document_len; + struct process_info_private *const priv = current_process ()->private; + char filename[PATH_MAX]; + int pid, is_elf64; + + 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; + + if (writebuf != NULL) + return -2; + if (readbuf == NULL) + return -1; + + 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; + + if (priv->r_debug == 0) + priv->r_debug = get_r_debug (pid, is_elf64); + + if (priv->r_debug == (CORE_ADDR) -1 || priv->r_debug == 0) + { + document = xstrdup ("<library-list-svr4 version=\"1.0\"/>\n"); + } + else + { + int allocated = 1024; + char *p; + const int ptr_size = is_elf64 ? 8 : 4; + CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_ld, l_next, l_prev; + int r_version, header_done = 0; + + document = xmalloc (allocated); + strcpy (document, "<library-list-svr4 version=\"1.0\""); + p = document + strlen (document); + + 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); + goto done; + } + + 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); + goto done; + } + + 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') + { + /* 6x the size for xml_escape_text below. */ + size_t len = 6 * strlen ((char *) libname); + char *name; + + if (!header_done) + { + /* Terminate `<library-list-svr4'. */ + *p++ = '>'; + header_done = 1; + } + + while (allocated < p - document + len + 200) + { + /* Expand to guarantee sufficient storage. */ + uintptr_t document_len = p - document; + + document = xrealloc (document, 2 * allocated); + allocated *= 2; + p = document + document_len; + } + + name = xml_escape_text ((char *) libname); + p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" " + "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>", + name, (unsigned long) lm_addr, + (unsigned long) l_addr, (unsigned long) l_ld); + free (name); + } + else if (lm_prev == 0) + { + sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr); + p = p + strlen (p); + } + + if (l_next == 0) + break; + + lm_prev = lm_addr; + lm_addr = l_next; + } + done: + strcpy (p, "</library-list-svr4>"); + } + + document_len = strlen (document); + if (offset < document_len) + document_len -= offset; + else + document_len = 0; + if (len > document_len) + len = document_len; + + memcpy (readbuf, document + offset, len); + xfree (document); + + return len; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -5026,6 +5402,7 @@ static struct target_ops linux_target_ops = { linux_emit_ops, linux_supports_disable_randomization, linux_get_min_fast_tracepoint_insn_len, + linux_qxfer_libraries_svr4, }; static void diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index a4e3d5a..0690b8e 100644 --- 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; diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 0f963e8..b90b9ce 100644 --- 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; + /* Do not confuse this packet with qXfer:libraries-svr4:read. */ + if (the_target->qxfer_libraries_svr4 != NULL) + return 0; + /* Over-estimate the necessary memory. Assume that every character in the library name must be escaped. */ total_len = 64; @@ -992,6 +996,23 @@ handle_qxfer_libraries (const char *annex, return len; } +/* Handle qXfer:libraries-svr4:read. */ + +static int +handle_qxfer_libraries_svr4 (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (writebuf != NULL) + return -2; + + if (annex[0] != '\0' || !target_running () + || the_target->qxfer_libraries_svr4 == NULL) + return -1; + + return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len); +} + /* Handle qXfer:osadata:read. */ static int @@ -1216,6 +1237,7 @@ static const struct qxfer qxfer_packets[] = { "fdpic", handle_qxfer_fdpic}, { "features", handle_qxfer_features }, { "libraries", handle_qxfer_libraries }, + { "libraries-svr4", handle_qxfer_libraries_svr4 }, { "osdata", handle_qxfer_osdata }, { "siginfo", handle_qxfer_siginfo }, { "spu", handle_qxfer_spu }, @@ -1536,9 +1558,14 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1); - /* We do not have any hook to indicate whether the target backend - supports qXfer:libraries:read, so always report it. */ - strcat (own_buf, ";qXfer:libraries:read+"); + if (the_target->qxfer_libraries_svr4 != NULL) + strcat (own_buf, ";qXfer:libraries-svr4:read+"); + else + { + /* We do not have any hook to indicate whether the non-SVR4 target + backend supports qXfer:libraries:read, so always report it. */ + strcat (own_buf, ";qXfer:libraries:read+"); + } if (the_target->read_auxv != NULL) strcat (own_buf, ";qXfer:auxv:read+"); diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 8a25476..0923892 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -390,6 +390,11 @@ struct target_ops /* Return the minimum length of an instruction that can be safely overwritten for use as a fast tracepoint. */ int (*get_min_fast_tracepoint_insn_len) (void); + + /* Read solib info on SVR4 platforms. */ + int (*qxfer_libraries_svr4) (const char *annex, unsigned char *readbuf, + unsigned const char *writebuf, + CORE_ADDR offset, int len); }; extern struct target_ops *the_target; |