[PATCH] Add OSE operating system support [3/5] remote relocation support

Hui Zhu hui_zhu@mentor.com
Fri Mar 8 13:30:00 GMT 2013


This is introduce of remote relocation support from Pedro:
The current qOffsets packet isn't good enough for OSE, for a few
reasons.  OSE elf binaries have an extra segment, in addition to
the usual code and data segments.  This segment contains something
like "load module configuration data", which is just a string of
configurations pertinent to the OSE loader.

Program Headers:
    Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
    LOAD           0x000718 0x0102ef8c 0x0102ef8c 0x00800 0x00800 R   0x4
    DYNAMIC        0x000f18 0x00000000 0x00000000 0x04500 0x00000 R   0x4
    LOAD           0x005418 0x01000000 0x01000000 0x2ef8c 0x2ef8c R E 0x10000
    LOAD           0x0343a4 0x01030000 0x01030000 0x00080 0x0082c RW  0x20

   Section to Segment mapping:
    Segment Sections...
     00     LMCONF
     01
     02     .text .rodata .eh_frame
     03     .data .sbss .bss


Sections:
Idx Name          Size      VMA       LMA       File off  Algn
    0 .text         00028c38  01000000  01000000  00005418  2**5
                    CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
    1 .ctors        00000000  01028c38  01028c38  00034424  2**0
                    CONTENTS
    2 .dtors        00000000  01028c38  01028c38  00034424  2**0
                    CONTENTS
    3 .rodata       00006304  01028c38  01028c38  0002e050  2**3
                    CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
    4 .eh_frame     00000050  0102ef3c  0102ef3c  00034354  2**2
                    CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
    5 .sdata2       00000000  0102ef8c  01030080  00034424  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    6 .data         00000080  01030000  01030000  000343a4  2**2
                    CONTENTS, ALLOC, LOAD, RELOC, DATA
    7 .sdata        00000000  01030080  01030080  00034424  2**2
                    CONTENTS, ALLOC, LOAD, DATA
    8 .sbss         0000000c  01030080  01030080  00034424  2**2
                    ALLOC
    9 .bss          0000078c  010300a0  010300a0  00034424  2**5
                    ALLOC
<snip dwarf sections>
...
   18 LMCONF        00000800  0102ef8c  0102ef8c  00000718  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
   19 .comment      00000828  00000000  00000000  0004f056  2**0
                    CONTENTS, READONLY
   20 .debug_ranges 00000018  00000000  00000000  0004f87e  2**0
                    CONTENTS, READONLY, DEBUGGING

The OSE debug API only gives us section load addresses,
not offsets, which is what qOffsets wants, when given sections
instead of segments.  OSE executables are fully linked binaries,
not relocatable objects, otherwise, offsets or absolute addresses
would be the same thing.  In any case, since there's an extra segment,
qOffsets wouldn't be able to relocate that extra segment correctly,
given only .text and .data offsets.

So, between extending qOffsets and coming up with something new,
I chose the latter.  I also thought of extending
qXfer:libraries:read to be able to report the main executable,
and extract the main executable's relocation info from that,
but I ended instead adding a new, but similar query.  I think
we could make it work, but it adds dependencies on which solib
backend is in use, and, it looks like it would complicate
the implementation quite a bit.


In a nutshell, the target reports either segment or section
absolute load addresses, in the same order as the segments or
allocated sections appear on the executable file.  From the
patch:

+@smallexample
+<load-map>
+  <segment address="0x10000000"/>
+</load-map>
+@end smallexample
+
+Another simple load map, for a program with three allocated sections
+(.text, .data, .bss), looks like this:
+
+@smallexample
+<load-map>
+  <section address="0x10000000"/>
+  <section address="0x20000000"/>
+  <section address="0x30000000"/>
+</load-map>
+@end smallexample


I thought of also adding support for reported section
offsets instead of addresses, and optional section names (as
an optional attribute), for the case of the debug
API not giving us the sections in the same order as in is in
binary.  But since we need to keep supporting qOffsets forever
anyway, I resisted adding that upfront until we find a need.
I don't actually know of any target that would need that.
We'd have seen a qOffsets extension/replacement sooner
if there'd been a hard need before, I'd think.

The possibility of adding optional xml attributes to the
dtd in the future, was what made me add the
xml_find_attribute function used like so:

+  struct load_map_info *info = user_data;
+  ULONGEST *address_p = xml_find_attribute (attributes, "address")->value;
+  CORE_ADDR address = (CORE_ADDR) *address_p;

instead of relying on attribute position, like we do in
other places parsing xml.  E.g., solib-target.c:

    struct lm_info *last = VEC_last (lm_info_p, *list);
    ULONGEST *address_p = VEC_index (gdb_xml_value_s, attributes, 0)->value;
    CORE_ADDR address = (CORE_ADDR) *address_p;

Sound like unnecessary hardcoding that way.

Comments on all of this, before I pass it upstream?  Maybe
there's some use case we've wished before qOffsets would
handle?

I've confirmed that OSE binaries are relocated correctly
with a patch that implements this packet in the OSE stub.


(BTW, this new packet started out spelled as "qXfer:offsets:read",
but it looked silly given that it doesn't support offsets as
of yet... load-map was the best I came up with, after considering
names like "qXfer:relocation-info:read".  Anyway, whatever name,
works for me.)

Thanks,
Hui

2013-03-08  Pedro Alves  <pedro@codesourcery.com>
	    Luis Machado  <lgustavo@codesourcery.com>

	* corelow.c (Makefile.in): Add load-map.dtd.
	* features/load-map.dtd: New.
	* remote.c (get_offsets): Removed.
	(PACKET_qXfer_load_map, load_map_start_segment,
	load_map_start_section, load_map_start, load_map_end,
	free_load_map, free_current_contents_load_map,
	segment_attributes, section_attributes, load_map_children,
	load_map_attributes, load_map_elements, parse_load_map,
	remote_relocate_using_qXfer_load_map,
	remote_relocate_using_qOffsets, remote_relocate): New.
	(remote_start_remote): Call remote_relocate.
	(remote_pr): Add qXfer:load-map:read.
	(extended_remote_attach_1): Call remote_relocate.
	(remote_xfer_partial): Handle TARGET_OBJECT_LOAD_MAP.
	(_initialize_remote): Add load-map command.

On 03/05/13 18:01, Hui Zhu wrote:
> Hi,
>
> This patch add relocation support to remote target.
> If target support qXfer:load-map:read, it use this packet to request a list of segment or section load addresses of the main executable.
> If target doesn't support it, use qOffsets that support in before request section offsets.
>
> Thanks,
> Hui
>
> 2013-03-05  Luis Machado  <lgustavo@codesourcery.com>
>
>      * corelow.c (Makefile.in): Add load-map.dtd.
>      * features/load-map.dtd: New.
>      * remote.c (get_offsets): Removed.
>      (PACKET_qXfer_load_map, load_map_start_segment,
>      load_map_start_section, load_map_start, load_map_end,
>      free_load_map, free_current_contents_load_map,
>      segment_attributes, section_attributes, load_map_children,
>      load_map_attributes, load_map_elements, parse_load_map,
>      remote_relocate_using_qXfer_load_map,
>      remote_relocate_using_qOffsets, remote_relocate): New.
>      (remote_start_remote): Call remote_relocate.
>      (remote_pr): Add qXfer:load-map:read.
>      (extended_remote_attach_1): Call remote_relocate.
>      (remote_xfer_partial): Handle TARGET_OBJECT_LOAD_MAP.
>      (_initialize_remote): Add load-map command.


-------------- next part --------------
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -496,8 +496,9 @@ RUNTESTFLAGS=
 # XML files to build in to GDB.
 XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
 	$(srcdir)/features/library-list.dtd \
-	$(srcdir)/features/library-list-svr4.dtd $(srcdir)/features/osdata.dtd \
-	$(srcdir)/features/threads.dtd $(srcdir)/features/traceframe-info.dtd
+	$(srcdir)/features/library-list-svr4.dtd $(srcdir)/features/load-map.dtd \
+	$(srcdir)/features/osdata.dtd $(srcdir)/features/threads.dtd \
+	$(srcdir)/features/traceframe-info.dtd
 
 # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
 # interface to the serial port.  Hopefully if get ported to OS/2, VMS,
--- /dev/null
+++ b/gdb/features/load-map.dtd
@@ -0,0 +1,15 @@
+<!-- Copyright (C) 2010 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- load-map: Root element with versioning -->
+<!ELEMENT load-map  (segment*, section*)>
+<!ATTLIST load-map  version CDATA   #FIXED  "1.0">
+
+<!ELEMENT segment       EMPTY>
+<!ATTLIST segment       address CDATA   #REQUIRED>
+
+<!ELEMENT section       EMPTY>
+<!ATTLIST section       address CDATA   #REQUIRED>
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -143,8 +143,6 @@ static void interrupt_query (void);
 static void set_general_thread (struct ptid ptid);
 static void set_continue_thread (struct ptid ptid);
 
-static void get_offsets (void);
-
 static void skip_frame (void);
 
 static long read_frame (char **buf_p, long *sizeof_buf);
@@ -1258,6 +1256,7 @@ enum {
   PACKET_qXfer_statictrace_read,
   PACKET_qXfer_traceframe_info,
   PACKET_qXfer_uib,
+  PACKET_qXfer_load_map,
   PACKET_qGetTIBAddr,
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
@@ -3031,10 +3030,203 @@ remote_close (int quitting)
   remote_notif_unregister_async_event_handler ();
 }
 
-/* Query the remote side for the text, data and bss offsets.  */
+#if defined(HAVE_LIBEXPAT)
+
+/* Handle the start of a <segment> element.  */
 
 static void
-get_offsets (void)
+load_map_start_segment (struct gdb_xml_parser *parser,
+			const struct gdb_xml_element *element,
+			void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct load_map_info *info = user_data;
+  ULONGEST *address_p = xml_find_attribute (attributes, "address")->value;
+  CORE_ADDR address = (CORE_ADDR) *address_p;
+
+  if (info->section_bases != NULL)
+    gdb_xml_error (parser,
+		   _("Library list with both segments and sections"));
+
+  VEC_safe_push (CORE_ADDR, info->section_bases, address);
+}
+
+/* Handle the start of a <section> element.  */
+
+static void
+load_map_start_section (struct gdb_xml_parser *parser,
+			const struct gdb_xml_element *element,
+			void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct load_map_info *info = user_data;
+  ULONGEST *address_p = xml_find_attribute (attributes, "address")->value;
+  CORE_ADDR address = (CORE_ADDR) *address_p;
+
+  if (info->segment_bases != NULL)
+    gdb_xml_error (parser,
+		   _("Offsets with both segments and sections"));
+
+  VEC_safe_push (CORE_ADDR, info->segment_bases, address);
+}
+
+/* Handle the start of a <load-map> element.  */
+
+static void
+load_map_start (struct gdb_xml_parser *parser,
+		const struct gdb_xml_element *element,
+		void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  char *version = xml_find_attribute (attributes, "version")->value;
+
+  if (strcmp (version, "1.0") != 0)
+    gdb_xml_error (parser,
+		   _("Offsets list has unsupported version \"%s\""),
+		   version);
+}
+
+/* Handle the end of a <load-map> element.  */
+
+static void
+load_map_end (struct gdb_xml_parser *parser,
+	      const struct gdb_xml_element *element,
+	      void *user_data, const char *body_text)
+{
+  struct load_map_info *load_map_info = user_data;
+
+  if (load_map_info->segment_bases == NULL
+      && load_map_info->section_bases == NULL)
+    gdb_xml_error (parser,
+		   _("No segment or section bases defined"));
+}
+
+/* Discard the constructed load map.  */
+
+static void
+free_load_map (void *p)
+{
+  struct load_map_info *info = p;
+
+  VEC_free (CORE_ADDR, info->segment_bases);
+  VEC_free (CORE_ADDR, info->section_bases);
+  xfree (info);
+}
+
+static void
+free_current_contents_load_map (void *p)
+{
+  struct load_map_info **info = p;
+
+  free_load_map (*info);
+  *info = NULL;
+}
+
+/* The allowed elements and attributes for an XML load map.  The root
+   element is a <load-map>.  */
+
+static const struct gdb_xml_attribute segment_attributes[] = {
+  { "address", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute section_attributes[] = {
+  { "address", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element load_map_children[] = {
+  { "segment", segment_attributes, NULL,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    load_map_start_segment, NULL },
+  { "section", section_attributes, NULL,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    load_map_start_section, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute load_map_attributes[] = {
+  { "version", GDB_XML_AF_NONE, NULL, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element load_map_elements[] = {
+  { "load-map", load_map_attributes, load_map_children,
+    GDB_XML_EF_NONE, load_map_start, load_map_end },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static struct load_map_info *
+parse_load_map (const char *load_map)
+{
+  struct load_map_info *result = XCNEW (struct load_map_info);
+  struct cleanup *back_to = make_cleanup (free_current_contents_load_map,
+					  &result);
+
+  if (gdb_xml_parse_quick (_("load map"), "load-map.dtd",
+			   load_map_elements, load_map, result) == 0)
+    {
+      /* Parsed successfully, keep the result.  */
+      discard_cleanups (back_to);
+      return result;
+    }
+
+  do_cleanups (back_to);
+  return NULL;
+}
+
+#endif
+
+/* Relocate the main symbol file and executable using the load
+   addresses as reported by a qXfer:load-map:read query.  Returns true
+   if the target supported the query, false otherwise.  We'll still
+   try qOffsets in the latter case.  */
+
+static int
+remote_relocate_using_qXfer_load_map (void)
+{
+#if defined(HAVE_LIBEXPAT)
+  if (remote_protocol_packets[PACKET_qXfer_load_map].support == PACKET_ENABLE)
+    {
+      struct section_offsets *offs;
+      const char *load_map_document;
+      struct load_map_info *info;
+      struct bfd *abfd;
+      const char *filename;
+      struct cleanup *old_chain;
+
+      /* Fetch the offset list.  */
+
+      load_map_document = target_read_stralloc (&current_target,
+						TARGET_OBJECT_LOAD_MAP,
+						NULL);
+      if (load_map_document == NULL)
+	return 0;
+
+      old_chain = make_cleanup (xfree, (void *) load_map_document);
+
+      /* Parse the list.  */
+      info = parse_load_map (load_map_document);
+      if (info == NULL)
+	{
+	  do_cleanups (old_chain);
+	  return 0;
+	}
+
+      make_cleanup (free_load_map, info);
+
+      relocate_with_load_map (info);
+
+      do_cleanups (old_chain);
+      return 1;
+    }
+#endif
+
+  return 0;
+}
+
+/* Query the remote side for the text, data and bss section offsets,
+   or text and data segment load bases.  */
+
+static void
+remote_relocate_using_qOffsets (void)
 {
   struct remote_state *rs = get_remote_state ();
   char *buf;
@@ -3044,9 +3236,6 @@ get_offsets (void)
   struct section_offsets *offs;
   struct symfile_segment_data *data;
 
-  if (symfile_objfile == NULL)
-    return;
-
   putpkt ("qOffsets");
   getpkt (&rs->buf, &rs->buf_size, 0);
   buf = rs->buf;
@@ -3190,6 +3379,17 @@ get_offsets (void)
   objfile_relocate (symfile_objfile, offs);
 }
 
+static void
+remote_relocate (void)
+{
+  if (symfile_objfile == NULL)
+    return;
+
+  if (remote_relocate_using_qXfer_load_map ())
+    return;
+  remote_relocate_using_qOffsets ();
+}
+
 /* Callback for iterate_over_threads.  Set the STOP_REQUESTED flags in
    threads we know are stopped already.  This is used during the
    initial remote connection in non-stop mode --- threads that are
@@ -3468,7 +3668,9 @@ remote_start_remote (int from_tty, struc
 	 manipulation.  */
       init_wait_for_inferior ();
 
-      get_offsets ();		/* Get text, data & bss offsets.  */
+      /* Get section/segment load offsets/addresses from the
+	 target.  */
+      remote_relocate ();
 
       /* If we could not find a description using qXfer, and we know
 	 how to do it some other way, try again.  This is not
@@ -3542,7 +3744,9 @@ remote_start_remote (int from_tty, struc
       if (ptid_equal (inferior_ptid, minus_one_ptid))
 	error (_("remote didn't report the current thread in non-stop mode"));
 
-      get_offsets ();		/* Get text, data & bss offsets.  */
+      /* Get section/segment load offsets/addresses from the
+	 target.  */
+      remote_relocate ();
 
       /* In non-stop mode, any cached wait status will be stored in
 	 the stop reply queue.  */
@@ -3950,6 +4154,8 @@ static struct protocol_feature remote_pr
     PACKET_qXfer_threads },
   { "qXfer:traceframe-info:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_traceframe_info },
+  { "qXfer:load-map:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_load_map },
   { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QPassSignals },
   { "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
@@ -4519,6 +4725,9 @@ extended_remote_attach_1 (struct target_
      this before anything involving memory or registers.  */
   target_find_description ();
 
+  /* Get updated relocation offsets.  */
+  remote_relocate ();
+
   if (!non_stop)
     {
       /* Use the previously fetched status.  */
@@ -7950,7 +8159,7 @@ extended_remote_create_inferior_1 (char
     {
       /* Clean up from the last time we ran, before we mark the target
 	 running again.  This will mark breakpoints uninserted, and
-	 get_offsets may insert breakpoints.  */
+	 remote_relocate may insert breakpoints.  */
       init_thread_list ();
       init_wait_for_inferior ();
     }
@@ -7959,8 +8168,8 @@ extended_remote_create_inferior_1 (char
   stop_reply = run_worked ? rs->buf : NULL;
   add_current_inferior_and_thread (stop_reply);
 
-  /* Get updated offsets, if the stub uses qOffsets.  */
-  get_offsets ();
+  /* Get section/segment load offsets/addresses from the target.  */
+  remote_relocate ();
 }
 
 static void
@@ -8796,6 +9005,10 @@ remote_xfer_partial (struct target_ops *
       return remote_read_qxfer (ops, "uib", annex, readbuf, offset, len,
 				&remote_protocol_packets[PACKET_qXfer_uib]);
 
+    case TARGET_OBJECT_LOAD_MAP:
+      gdb_assert (annex == NULL);
+      return remote_read_qxfer (ops, "load-map", annex, readbuf, offset, len,
+				&remote_protocol_packets[PACKET_qXfer_load_map]);
     default:
       return -1;
     }
@@ -11663,6 +11876,9 @@ Show the maximum size of the address (in
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_uib],
 			 "qXfer:uib:read", "unwind-info-block", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_load_map],
+			 "qXfer:load-map:read", "load-map", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr],
 			 "qGetTLSAddr", "get-thread-local-storage-address",
 			 0);



More information about the Binutils mailing list