Index: libiberty/simple-object-mach-o.c
===================================================================
--- libiberty/simple-object-mach-o.c	(revision 179332)
+++ libiberty/simple-object-mach-o.c	(working copy)
@@ -170,9 +170,14 @@ struct mach_o_section_64
 
 #define MACH_O_NAME_LEN (16)
 
-/* A GNU specific extension for long section names.  */
+/* A GNU-specific extension to wrap multiple sections using three 
+   mach-o sections within a given segment.  The section '__wrapper_sects'
+   is subdivided according to the index '__wrapper_index' and each sub
+   sect is named according to the names supplied in '__wrapper_names'.  */
 
-#define GNU_SECTION_NAMES "__section_names"
+#define GNU_WRAPPER_SECTS "__wrapper_sects"
+#define GNU_WRAPPER_INDEX "__wrapper_index"
+#define GNU_WRAPPER_NAMES "__wrapper_names"
 
 /* Private data for an simple_object_read.  */
 
@@ -214,8 +219,19 @@ struct simple_object_mach_o_attributes
   unsigned int reserved;
 };
 
-/* See if we have a Mach-O file.  */
+/* See if we have a Mach-O MH_OBJECT file:
 
+   A standard MH_OBJECT (from as) will have three load commands:
+   0 - LC_SEGMENT/LC_SEGMENT64
+   1 - LC_SYMTAB
+   2 - LC_DYSYMTAB
+   
+   The LC_SEGMENT/LC_SEGMENT64 will introduce a single anonymous segment
+   containing all the sections.
+   
+   Files written by simple-object will have only the segment command
+   (no symbol tables).  */
+
 static void *
 simple_object_mach_o_match (
     unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
@@ -258,18 +274,10 @@ simple_object_mach_o_match (
     }
 #endif
 
-  /* We require the user to provide a segment name.  This is
-     unfortunate but I don't see any good choices here.  */
-
-  if (segment_name == NULL)
+  /* Although a standard MH_OBJECT has a single anonymous segment, we allow
+     a segment name to be specified - but don't act on it at this stage.  */
+  if (segment_name && strlen (segment_name) > MACH_O_NAME_LEN)
     {
-      *errmsg = "Mach-O file found but no segment name specified";
-      *err = 0;
-      return NULL;
-    }
-
-  if (strlen (segment_name) > MACH_O_NAME_LEN)
-    {
       *errmsg = "Mach-O segment name too long";
       *err = 0;
       return NULL;
@@ -294,13 +302,14 @@ simple_object_mach_o_match (
   filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype));
   if (filetype != MACH_O_MH_OBJECT)
     {
-      *errmsg = "Mach-O file is not object file";
+      *errmsg = "Mach-O file is not an MH_OBJECT file";
       *err = 0;
       return NULL;
     }
 
   omr = XNEW (struct simple_object_mach_o_read);
-  omr->segment_name = xstrdup (segment_name);
+  if (segment_name)
+    omr->segment_name = xstrdup (segment_name);
   omr->magic = magic;
   omr->is_big_endian = is_big_endian;
   omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype));
@@ -356,9 +365,25 @@ simple_object_mach_o_section_info (int is_big_endi
     }
 }
 
-/* Handle a segment in a Mach-O file.  Return 1 if we should continue,
-   0 if the caller should return.  */
+/* Handle a segment in a Mach-O Object file.
 
+   This will callback to the function pfn for each "section found" the meaning
+   of which depends on a gnu extension to mach-o:
+   
+   When omr->segment_name is specified, we implement a wrapper scheme (which
+   whilst it will, in principle, work with any segment name, is likely to be
+   meaningless current for anything except __GNU_LTO).
+   
+   If we find mach-o sections (with the segment name as specified) which also
+   contain: a 'sects' wrapper, an index, and a  name table, we expand this into
+   as many sections as are specified in the index.  In this case, there will
+   be a callback for each of these.
+   
+   When the omr->segment_name is not specified, then we would call-back for
+   every mach-o section specified in the file.
+
+   Return 1 if we should continue, 0 if the caller should return.  */
+
 static int
 simple_object_mach_o_segment (simple_object_read *sobj, off_t offset,
 			      const unsigned char *segbuf,
@@ -375,12 +400,19 @@ simple_object_mach_o_segment (simple_object_read *
   size_t sechdrsize;
   size_t segname_offset;
   size_t sectname_offset;
-  unsigned int nsects;
-  unsigned char *secdata;
-  unsigned int i;
-  unsigned int strtab_index;
-  char *strtab;
-  size_t strtab_size;
+  int nsects;
+  unsigned char *secdata = NULL;
+  int i;
+  int strtab_index, index_index, sections_index;
+  char *strtab = NULL;
+  size_t strtab_size = 0;
+  unsigned char *index = NULL;
+  size_t index_size;
+  unsigned int n_wrapped_sects = 0;
+  size_t wrapper_sect_size = 0;
+  off_t wrapper_sect_offset = (off_t)0;
+  int wrapping = (omr->segment_name && strlen (omr->segment_name) > 0);
+  strtab_index = index_index = sections_index = -1;
 
   fetch_32 = (omr->is_big_endian
 	      ? simple_object_fetch_big_32
@@ -409,6 +441,7 @@ simple_object_mach_o_segment (simple_object_read *
 					nsects));
     }
 
+  /* Fetch the section headers from the segment command.  */
   secdata = XNEWVEC (unsigned char, nsects * sechdrsize);
   if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize,
 				    secdata, nsects * sechdrsize, errmsg, err))
@@ -417,87 +450,147 @@ simple_object_mach_o_segment (simple_object_read *
       return 0;
     }
 
-  /* Scan for a __section_names section.  This is in effect a GNU
-     extension that permits section names longer than 16 chars.  */
-
-  for (i = 0; i < nsects; ++i)
+  /* If the user has set a segment name to be processed for GNU sub-sections
+     then scan for the three relevant sections.  */
+  if (wrapping)
     {
-      size_t nameoff;
+      off_t strtab_offset;
+      off_t index_offset;
 
-      nameoff = i * sechdrsize + segname_offset;
-      if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0)
-	continue;
-      nameoff = i * sechdrsize + sectname_offset;
-      if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0)
-	break;
-    }
+      for (i = 0; i < nsects; ++i)
+	{
+	  size_t nameoff;
 
-  strtab_index = i;
-  if (strtab_index >= nsects)
-    {
-      strtab = NULL;
-      strtab_size = 0;
-    }
-  else
-    {
-      off_t strtab_offset;
+	nameoff = i * sechdrsize + segname_offset;
+	if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0)
+	  continue;
 
-      simple_object_mach_o_section_info (omr->is_big_endian, is_32,
-					 secdata + strtab_index * sechdrsize,
-					 &strtab_offset, &strtab_size);
-      strtab = XNEWVEC (char, strtab_size);
-      if (!simple_object_internal_read (sobj->descriptor,
-					sobj->offset + strtab_offset,
-					(unsigned char *) strtab, strtab_size,
+	nameoff = i * sechdrsize + sectname_offset;
+	if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0)
+	  strtab_index = i;
+	else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0)
+	  index_index = i;
+	else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0)
+	  sections_index = i;
+      }
+
+      /* If any of the special wrapper section components is present, then
+	 they all should be.  */
+      if (sections_index >= 0 || index_index >= 0 || strtab_index >= 0)
+	{
+	  if (sections_index < 0 || index_index < 0 || strtab_index < 0)
+	    {
+	      *errmsg = "GNU Mach-o section wrapper: required section missing";
+	      *err = 0; /* No useful errno.  */
+	      XDELETEVEC (secdata);
+	      return 0;
+	    }
+
+	  /* Fetch the name table.  */
+	  simple_object_mach_o_section_info (omr->is_big_endian, is_32,
+					     secdata + strtab_index * sechdrsize,
+					     &strtab_offset, &strtab_size);
+	  strtab = XNEWVEC (char, strtab_size);
+	  if (!simple_object_internal_read (sobj->descriptor,
+					    sobj->offset + strtab_offset,
+					    (unsigned char *) strtab, strtab_size,
+					    errmsg, err))
+	    {
+	      XDELETEVEC (strtab);
+	      XDELETEVEC (secdata);
+	      return 0;
+	    }
+
+	  /* Fetch the index.  */
+	  simple_object_mach_o_section_info (omr->is_big_endian, is_32,
+					 secdata + index_index * sechdrsize,
+					 &index_offset, &index_size);
+	  index = XNEWVEC (unsigned char, index_size);
+	  if (!simple_object_internal_read (sobj->descriptor,
+					sobj->offset + index_offset,
+					index, index_size,
 					errmsg, err))
-	{
-	  XDELETEVEC (strtab);
-	  XDELETEVEC (secdata);
-	  return 0;
+	    {
+	      XDELETEVEC (index);
+	      XDELETEVEC (strtab);
+	      XDELETEVEC (secdata);
+	      return 0;
+	   }
+
+	  /* The index contains 4 uint32_t per sub-section:
+	     sub-section offset/length, sub-section name/length.  
+	     We fix this for both 32 and 64 bit mach-o for now, since
+	     other fields limit the maximum size of an object to 4G.  */
+	  n_wrapped_sects = index_size / 16;
+
+	  /* Get the parameters for the wrapper too.  */
+	  simple_object_mach_o_section_info (omr->is_big_endian, is_32,
+				       secdata + sections_index * sechdrsize,
+				       &wrapper_sect_offset, 
+				       &wrapper_sect_size);
+
 	}
+      else
+	wrapping = 0; /* We thought we were, but no wrapped sections found.  */
     }
 
   /* Process the sections.  */
-
   for (i = 0; i < nsects; ++i)
     {
       const unsigned char *sechdr;
-      char namebuf[MACH_O_NAME_LEN + 1];
+      char namebuf[MACH_O_NAME_LEN*2 + 2]; /* space for segment,section\0.  */
       char *name;
       off_t secoffset;
       size_t secsize;
+      int l;
 
-      if (i == strtab_index)
-	continue;
-
       sechdr = secdata + i * sechdrsize;
+      /* Process sections associated with the wrapper.  */
+      if (wrapping)
+	{
+	  if (i == strtab_index || i == index_index)
+	    continue;
 
-      if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0)
-	continue;
+	  if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0)
+	    continue;
 
-      memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN);
-      namebuf[MACH_O_NAME_LEN] = '\0';
-
-      name = &namebuf[0];
-      if (strtab != NULL && name[0] == '_' && name[1] == '_')
-	{
-	  unsigned long stringoffset;
-
-	  if (sscanf (name + 2, "%08lX", &stringoffset) == 1)
+	  if (i == sections_index)
 	    {
-	      if (stringoffset >= strtab_size)
+	      int j;
+	      for (j = 0; j < n_wrapped_sects; ++j)
 		{
-		  *errmsg = "section name offset out of range";
-		  *err = 0;
-		  XDELETEVEC (strtab);
-		  XDELETEVEC (secdata);
-		  return 0;
+		  unsigned int subsect_offset, subsect_length, name_offset;
+		  subsect_offset = (*fetch_32) (index + 16 * j);
+		  subsect_length = (*fetch_32) (index + 16 * j + 4);
+		  name_offset = (*fetch_32) (index + 16 * j + 8);
+		  /* We don't need the name_length yet.  */
+	  
+		  secoffset = wrapper_sect_offset + subsect_offset;
+		  secsize = subsect_length;
+		  name = strtab + name_offset;
+
+		  if (!(*pfn) (data, name, secoffset, secsize))
+		    {
+		      *errmsg = NULL;
+		      *err = 0;
+		      XDELETEVEC (index);
+		      XDELETEVEC (strtab);
+		      XDELETEVEC (secdata);
+		      return 0;
+		    }
 		}
-
-	      name = strtab + stringoffset;
+	      continue;
 	    }
 	}
 
+      /* Otherwise, make a name like __segment,__section as per the convention
+         in mach-o asm.  */
+      memset (namebuf, 0, MACH_O_NAME_LEN*2+2);
+      memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN);
+      l = strlen (namebuf);
+      namebuf[l] = ',';
+      memcpy (namebuf, (char *) sechdr + sectname_offset, MACH_O_NAME_LEN);
+
       simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr,
 					 &secoffset, &secsize);
 
@@ -505,12 +598,14 @@ simple_object_mach_o_segment (simple_object_read *
 	{
 	  *errmsg = NULL;
 	  *err = 0;
+	  XDELETEVEC (index);
 	  XDELETEVEC (strtab);
 	  XDELETEVEC (secdata);
 	  return 0;
 	}
     }
 
+  XDELETEVEC (index);
   XDELETEVEC (strtab);
   XDELETEVEC (secdata);
 
@@ -724,9 +819,9 @@ static int
 simple_object_mach_o_write_section_header (simple_object_write *sobj,
 					   int descriptor,
 					   size_t sechdr_offset,
-					   const char *name, size_t secaddr,
-					   size_t secsize, size_t offset,
-					   unsigned int align,
+					   const char *name, const char *segn,
+					   size_t secaddr, size_t secsize,
+					   size_t offset, unsigned int align,
 					   const char **errmsg, int *err)
 {
   struct simple_object_mach_o_attributes *attrs =
@@ -748,7 +843,7 @@ simple_object_mach_o_write_section_header (simple_
       strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname),
 	       name, MACH_O_NAME_LEN);
       strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname),
-	       sobj->segment_name, MACH_O_NAME_LEN);
+	       segn, MACH_O_NAME_LEN);
       set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr);
       set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize);
       set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset);
@@ -773,7 +868,7 @@ simple_object_mach_o_write_section_header (simple_
       strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname),
 	       name, MACH_O_NAME_LEN);
       strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname),
-	       sobj->segment_name, MACH_O_NAME_LEN);
+	       segn, MACH_O_NAME_LEN);
       set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr);
       set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize);
       set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset);
@@ -793,11 +888,26 @@ simple_object_mach_o_write_section_header (simple_
 				       sechdrsize, errmsg, err);
 }
 
-/* Write out the single segment and the sections of a Mach-O file.  */
+/* Write out the single (anonymous) segment containing the sections of a Mach-O
+   Object file.
+   
+   As a GNU extension to mach-o, when the caller specifies a segment name in
+   sobj->segment_name, all the sections passed will be output under a single
+   mach-o section header.  The caller's sections are indexed within this 
+   'wrapper' section by a table stored in a second mach-o section.  Finally,
+   arbitrary length section names are permitted by the extension and these are
+   stored in a table in a third mach-o section.
+   
+   Note that this is only likely to make any sense for the __GNU_LTO segment
+   at present.
+   
+   If the segment_name is not specified (or zero-length) we just write out the 
+   sections as we find them.  In that case we assume that the section name is 
+   in the form __SEGMENT_NAME,__section_name as per Mach-O asm.  */
 
 static int
 simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor,
-				    size_t nsects, const char **errmsg,
+				    size_t *nsects, const char **errmsg,
 				    int *err)
 {
   struct simple_object_mach_o_attributes *attrs =
@@ -814,6 +924,11 @@ simple_object_mach_o_write_segment (simple_object_
   simple_object_write_section *section;
   unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)];
   unsigned char *hdr;
+  int wrapping = (sobj->segment_name && strlen (sobj->segment_name) > 0);
+  size_t nsects_in;
+  uint32_t *index = NULL;
+  char *snames = NULL;
+  unsigned int sect;
 
   set_32 = (attrs->is_big_endian
 	    ? simple_object_set_big_32
@@ -834,19 +949,52 @@ simple_object_mach_o_write_segment (simple_object_
       sechdrsize = sizeof (struct mach_o_section_64);
     }
 
+  name_offset = 0;
+  *nsects = nsects_in = 0;
+  
+  /* Count the number of sections we start with.  */
+  for (section = sobj->sections; section != NULL;section = section->next)
+    nsects_in++;
+
+  if (wrapping)
+    {
+      /* We will only write 3 sections: wrapped data, index and names.  */
+      *nsects = 3; 
+      /* The index has four entries per wrapped section:
+	   Section Offset, length,  Name offset, length.
+	 Where the offsets are based at the start of the wrapper and name
+	 sections respectively.
+	 The values are stored as uint32 for both 32 and 64 bit mach-o
+	 since the size of a mach-o MH_OBJECT cannot exceed 4G owing to 
+	 other constraints.  */
+      index = XNEWVEC (uint32_t, nsects_in * 4);
+      /* We now need to figure out the size of the names section.  This just
+	 stores the names as null-terminated c strings, packed without any
+	 alignment padding.  */
+      for (section = sobj->sections, sect = 0; section != NULL;
+	   section = section->next, sect++)
+	{
+	  index[sect*4+2] = name_offset;
+	  index[sect*4+3] = strlen (section->name) + 1;
+	  name_offset += strlen (section->name) + 1;
+	}
+      snames = XNEWVEC (char, name_offset);
+    }
+  else
+    *nsects = nsects_in;
+
   sechdr_offset = hdrsize + seghdrsize;
-  cmdsize = seghdrsize + nsects * sechdrsize;
+  cmdsize = seghdrsize + *nsects * sechdrsize;
   offset = hdrsize + cmdsize;
-  name_offset = 0;
   secaddr = 0;
 
-  for (section = sobj->sections; section != NULL; section = section->next)
+  for (section = sobj->sections, sect = 0; section != NULL; 
+       section = section->next, sect++)
     {
       size_t mask;
       size_t new_offset;
       size_t secsize;
       struct simple_object_write_section_buffer *buffer;
-      char namebuf[MACH_O_NAME_LEN + 1];
 
       mask = (1U << section->align) - 1;
       new_offset = offset + mask;
@@ -877,39 +1025,117 @@ simple_object_mach_o_write_segment (simple_object_
 	  secsize += buffer->size;
 	}
 
-      snprintf (namebuf, sizeof namebuf, "__%08X", name_offset);
+      if (wrapping)
+	{
+	  index[sect*4+0] = (uint32_t) offset;
+	  index[sect*4+1] = secsize;
+	  /* Stash the section name in our table.  */
+	  memcpy (snames + index[sect*4+2], section->name, index[sect*4+3]);
+	}
+      else
+	{
+	  char namebuf[MACH_O_NAME_LEN + 1];
+	  char segnbuf[MACH_O_NAME_LEN + 1];
+	  char *comma;
+	  /* Try to extract segment,section from the input name.  */
+	  memset (namebuf, 0, sizeof namebuf);
+	  memset (segnbuf, 0, sizeof segnbuf);
+	  comma = strchr (section->name, ',');
+	  if (comma)
+	    {
+	      int len = comma-section->name;
+	      len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len;
+	      if (len)
+	        strncpy (namebuf, section->name, len);
+	      strncpy (segnbuf, comma+1, MACH_O_NAME_LEN);
+	    }
+	  else /* just try to copy the name, leave segment blank.  */
+	    strncpy (namebuf, section->name, MACH_O_NAME_LEN);
+
+	  if (!simple_object_mach_o_write_section_header (sobj, descriptor,
+							  sechdr_offset, 
+							  namebuf, segnbuf,
+							  secaddr, secsize,
+							  offset, 
+							  section->align,
+							  errmsg, err))
+	    return 0;
+	  sechdr_offset += sechdrsize;
+	}
+
+      offset += secsize;
+      secaddr += secsize;
+    }
+
+  if (wrapping)
+    {
+      size_t secsize;
+      int i;
+      /* Write the section header for the wrapper.  */
+      /* Account for any initial aligment - which becomes the alignment for this
+	 created section.  */
+      secsize = (offset - index[0]); 
       if (!simple_object_mach_o_write_section_header (sobj, descriptor,
-						      sechdr_offset, namebuf,
-						      secaddr, secsize, offset,
-						      section->align,
+						      sechdr_offset, 
+						      GNU_WRAPPER_SECTS,
+						      sobj->segment_name,
+						      0 /*secaddr*/, 
+						      secsize, 
+						      index[0] /* initial offset*/,
+						      sobj->sections->align,
 						      errmsg, err))
 	return 0;
 
+      /* Subtract the wrapper section start from the begining of each sub
+	 section.  */
+      for (i=1; i<nsects_in;i++)
+	index[4*i] -= index[0];
+      index[0] = 0;
+      
       sechdr_offset += sechdrsize;
-      offset += secsize;
-      name_offset += strlen (section->name) + 1;
-      secaddr += secsize;
-    }
+      /* Write out the section names.  
+	 ... the header ...
+	 name_offset contains the length of the section.  It is not aligned.  */
+	 
+      if (!simple_object_mach_o_write_section_header (sobj, descriptor,
+						      sechdr_offset,
+						      GNU_WRAPPER_NAMES,
+						      sobj->segment_name,
+						      0 /*secaddr*/, name_offset, 
+						      offset,
+						      0, errmsg, err))
+	return 0;
 
-  /* Write out the section names.  */
+      /* ... and the content.. */
+      if (!simple_object_internal_write (descriptor, offset,
+					 (const unsigned char *) snames,
+					 name_offset, errmsg, err))
+	return 0;
 
-  if (!simple_object_mach_o_write_section_header (sobj, descriptor,
-						  sechdr_offset,
-						  GNU_SECTION_NAMES, secaddr,
-						  name_offset, offset, 0,
-						  errmsg, err))
-    return 0;
+      sechdr_offset += sechdrsize;
+      secaddr += name_offset;
+      offset += name_offset;
 
-  for (section = sobj->sections; section != NULL; section = section->next)
-    {
-      size_t namelen;
-
-      namelen = strlen (section->name) + 1;
+      /* Now do the index, we'll align this to 4 bytes although the read code
+	 will handle unaligned.  */
+      offset += 3; offset &= ~0x03;
+      if (!simple_object_mach_o_write_section_header (sobj, descriptor,
+						      sechdr_offset,
+						      GNU_WRAPPER_INDEX,
+						      sobj->segment_name,
+						      0 /*secaddr*/, nsects_in*16, 
+						      offset,
+						      2, errmsg, err))
+	return 0;
+      
+      /* ... and the content.. */
       if (!simple_object_internal_write (descriptor, offset,
-					 (const unsigned char *) section->name,
-					 namelen, errmsg, err))
+					 (const unsigned char *) index,
+					 nsects_in*16, errmsg, err))
 	return 0;
-      offset += namelen;
+
+      XDELETEVEC (index);
+      XDELETEVEC (snames);
     }
 
   /* Write out the segment header.  */
@@ -923,9 +1149,8 @@ simple_object_mach_o_write_segment (simple_object_
 	      MACH_O_LC_SEGMENT);
       set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize),
 	      cmdsize);
-      strncpy (((char *) hdr
-		+ offsetof (struct mach_o_segment_command_32, segname)),
-	       sobj->segment_name, MACH_O_NAME_LEN);
+     /* MH_OBJECTS have a single, anonymous, segment - so the segment name
+	 is left empty.  */
       /* vmaddr left as zero.  */
       /* vmsize left as zero.  */
       set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff),
@@ -935,7 +1160,7 @@ simple_object_mach_o_write_segment (simple_object_
       /* maxprot left as zero.  */
       /* initprot left as zero.  */
       set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects),
-	      nsects);
+	      *nsects);
       /* flags left as zero.  */
     }
   else
@@ -951,9 +1176,8 @@ simple_object_mach_o_write_segment (simple_object_
 	      MACH_O_LC_SEGMENT);
       set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize),
 	      cmdsize);
-      strncpy (((char *) hdr
-		+ offsetof (struct mach_o_segment_command_64, segname)),
-	       sobj->segment_name, MACH_O_NAME_LEN);
+      /* MH_OBJECTS have a single, anonymous, segment - so the segment name
+	 is left empty.  */
       /* vmaddr left as zero.  */
       /* vmsize left as zero.  */
       set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff),
@@ -963,7 +1187,7 @@ simple_object_mach_o_write_segment (simple_object_
       /* maxprot left as zero.  */
       /* initprot left as zero.  */
       set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects),
-	      nsects);
+	      *nsects);
       /* flags left as zero.  */
 #endif
     }
@@ -978,23 +1202,17 @@ static const char *
 simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor,
 				    int *err)
 {
-  size_t nsects;
-  simple_object_write_section *section;
+  size_t nsects = 0;
   const char *errmsg;
 
-  /* Start at 1 for symbol_names section.  */
-  nsects = 1;
-  for (section = sobj->sections; section != NULL; section = section->next)
-    ++nsects;
+  if (!simple_object_mach_o_write_segment (sobj, descriptor, &nsects,
+					   &errmsg, err))
+    return errmsg;
 
   if (!simple_object_mach_o_write_header (sobj, descriptor, nsects,
 					  &errmsg, err))
     return errmsg;
 
-  if (!simple_object_mach_o_write_segment (sobj, descriptor, nsects,
-					   &errmsg, err))
-    return errmsg;
-
   return NULL;
 }
 
Index: gcc/config/darwin.c
===================================================================
--- gcc/config/darwin.c	(revision 179332)
+++ gcc/config/darwin.c	(working copy)
@@ -1753,19 +1753,51 @@ darwin_label_is_anonymous_local_objc_name (const c
   return (!strncmp ((const char *)p, "_OBJC_", 6));
 }
 
-/* LTO support for Mach-O.  */
+/* LTO support for Mach-O.
 
-/* Section names for LTO sections.  */
-static unsigned int lto_section_names_offset = 0;
+   This version uses three mach-o sections to encapsulate the (unlimited
+   number of) lto sections.
+   
+   __GNU_LTO, __lto_sections  contains the concatented GNU LTO section data.
+   __GNU_LTO, __section_names contains the GNU LTO section names.
+   __GNU_LTO, __section_index contains an array of values that index these.
 
-/* This is the obstack which we use to allocate the many strings.  */
-static struct obstack lto_section_names_obstack;
+   Indexed thus:
+     <section offset from the start of __GNU_LTO, __lto_sections>,
+     <section length>
+     <name offset from the start of __GNU_LTO, __section_names,
+     <name length>.
 
-/* Segment name for LTO sections.  */
+   At present, for both m32 and m64 mach-o files each of these fields is
+   represented  by a uint32_t.  This is because, AFAICT, a mach-o object
+   cannot exceed 4Gb because the section_64 offset field (see below) is 32bits.
+   
+    uint32_t offset;
+   "offset  An integer specifying the offset to this section in the file."  */
+
+/* Count lto section numbers. */
+static unsigned int lto_section_num = 0;
+
+/* A vector of information about LTO sections, at present, we only have
+   the name.  TODO: see if we can get the data length somehow.  */
+typedef struct GTY(()) darwin_lto_section_e {
+  const char *sectname;
+} darwin_lto_section_e ;
+DEF_VEC_O(darwin_lto_section_e);
+DEF_VEC_ALLOC_O(darwin_lto_section_e, gc);
+
+static GTY (()) VEC (darwin_lto_section_e, gc) * lto_section_names;
+
+/* Segment for LTO data.  */
 #define LTO_SEGMENT_NAME "__GNU_LTO"
 
-/* Section name for LTO section names section.  */
-#define LTO_NAMES_SECTION "__section_names"
+/* Section wrapper scheme (used here to wrap the unlimited number of LTO
+   sections into three Mach-O ones).
+   NOTE: These names MUST be kept in sync with those in 
+	 libiberty/simple-object-mach-o. */
+#define LTO_SECTS_SECTION "__wrapper_sects"
+#define LTO_NAMES_SECTION "__wrapper_names"
+#define LTO_INDEX_SECTION "__wrapper_index"
 
 /* File to temporarily store LTO data.  This is appended to asm_out_file
    in darwin_end_file.  */
@@ -1808,37 +1840,37 @@ darwin_asm_named_section (const char *name,
 			  unsigned int flags,
 			  tree decl ATTRIBUTE_UNUSED)
 {
-  /* LTO sections go in a special segment __GNU_LTO.  We want to replace the
-     section name with something we can use to represent arbitrary-length
-     names (section names in Mach-O are at most 16 characters long).  */
+  /* LTO sections go in a special section that encapsulates the (unlimited)
+     number of GNU LTO sections within a single mach-o one.  */
   if (strncmp (name, LTO_SECTION_NAME_PREFIX,
 	       strlen (LTO_SECTION_NAME_PREFIX)) == 0)
     {
+      darwin_lto_section_e e;
       /* We expect certain flags to be set...  */
       gcc_assert ((flags & (SECTION_DEBUG | SECTION_NAMED))
 		  == (SECTION_DEBUG | SECTION_NAMED));
 
-      /* Add the section name to the things to output when we end the
-	 current assembler output file.
-	 This is all not very efficient, but that doesn't matter -- this
-	 shouldn't be a hot path in the compiler...  */
-      obstack_1grow (&lto_section_names_obstack, '\t');
-      obstack_grow (&lto_section_names_obstack, ".ascii ", 7);
-      obstack_1grow (&lto_section_names_obstack, '"');
-      obstack_grow (&lto_section_names_obstack, name, strlen (name));
-      obstack_grow (&lto_section_names_obstack, "\\0\"\n", 4);
-
-      /* Output the dummy section name.  */
-      fprintf (asm_out_file, "\t# %s\n", name);
-      fprintf (asm_out_file, "\t.section %s,__%08X,regular,debug\n",
-	       LTO_SEGMENT_NAME, lto_section_names_offset);
-
-      /* Update the offset for the next section name.  Make sure we stay
-	 within reasonable length.  */  
-      lto_section_names_offset += strlen (name) + 1;
-      gcc_assert (lto_section_names_offset > 0
-		  && lto_section_names_offset < ((unsigned) 1 << 31));
-    }
+      /* Switch to our combined section.  */
+      fprintf (asm_out_file, "\t.section %s,%s,regular,debug\n",
+	       LTO_SEGMENT_NAME, LTO_SECTS_SECTION);
+      /* Output a label for the start of this sub-section.  */
+      fprintf (asm_out_file, "L_GNU_LTO%d:\t;# %s\n",
+	       lto_section_num, name);
+      /* We have to jump through hoops to get the values of the intra-section
+         offsets... */
+      fprintf (asm_out_file, "\t.set L$gnu$lto$offs%d,L_GNU_LTO%d-L_GNU_LTO0\n",
+	       lto_section_num, lto_section_num);
+      fprintf (asm_out_file, "\t.set L$gnu$lto$size%d,L_GNU_LTO%d-L_GNU_LTO%d\n",
+	       lto_section_num, lto_section_num+1, lto_section_num);
+      lto_section_num++;
+      e.sectname = xstrdup (name);
+      /* Keep the names, we'll need to make a table later.
+         TODO: check that we do not revisit sections, that would break 
+         the assumption of how this is done. */
+      if (lto_section_names == NULL)
+        lto_section_names = VEC_alloc (darwin_lto_section_e, gc, 16);
+      VEC_safe_push (darwin_lto_section_e, gc, lto_section_names, &e);
+   }
   else if (strncmp (name, "__DWARF,", 8) == 0)
     darwin_asm_dwarf_section (name, flags, decl);
   else
@@ -2711,16 +2743,12 @@ darwin_asm_output_dwarf_offset (FILE *file, int si
   darwin_asm_output_dwarf_delta (file, size, lab, sname);
 }
 
-/* Called from the within the TARGET_ASM_FILE_START for each target. 
-  Initialize the stuff we need for LTO long section names support.  */
+/* Called from the within the TARGET_ASM_FILE_START for each target.  */
 
 void
 darwin_file_start (void)
 {
-  /* We fill this obstack with the complete section text for the lto section
-     names to write in darwin_file_end.  */
-  obstack_init (&lto_section_names_obstack);
-  lto_section_names_offset = 0;
+  /* Nothing to do.  */
 }
 
 /* Called for the TARGET_ASM_FILE_END hook.
@@ -2731,8 +2759,6 @@ darwin_file_start (void)
 void
 darwin_file_end (void)
 {
-  const char *lto_section_names;
-
   machopic_finish (asm_out_file);
   if (strcmp (lang_hooks.name, "GNU C++") == 0)
     {
@@ -2762,6 +2788,13 @@ darwin_file_end (void)
 	  lto_asm_txt = buf = (char *) xmalloc (n + 1);
 	  while (fgets (lto_asm_txt, n, lto_asm_out_file))
 	    fputs (lto_asm_txt, asm_out_file);
+	  /* Put a termination label.  */
+	  fprintf (asm_out_file, "\t.section %s,%s,regular,debug\n",
+		   LTO_SEGMENT_NAME, LTO_SECTS_SECTION);
+	  fprintf (asm_out_file, "L_GNU_LTO%d:\t;# end of lto\n",
+		   lto_section_num);
+	  /* Make sure our termination label stays in this section.  */
+	  fputs ("\t.space\t1\n", asm_out_file);
 	}
 
       /* Remove the temporary file.  */
@@ -2770,21 +2803,49 @@ darwin_file_end (void)
       free (lto_asm_out_name);
     }
 
-  /* Finish the LTO section names obstack.  Don't output anything if
-     there are no recorded section names.  */
-  obstack_1grow (&lto_section_names_obstack, '\0');
-  lto_section_names = XOBFINISH (&lto_section_names_obstack, const char *);
-  if (strlen (lto_section_names) > 0)
+  /* Output the names and indices.  */
+  if (lto_section_names && VEC_length (darwin_lto_section_e, lto_section_names))
     {
-      fprintf (asm_out_file,
-	       "\t.section %s,%s,regular,debug\n",
+      int count;
+      darwin_lto_section_e *ref;
+      /* For now, we'll make the offsets 4 bytes and unaligned - we'll fix
+         the latter up ourselves. */
+      const char *op = integer_asm_op (4,0); 
+
+      /* Emit the names. */
+      fprintf (asm_out_file, "\t.section %s,%s,regular,debug\n",
 	       LTO_SEGMENT_NAME, LTO_NAMES_SECTION);
-      fprintf (asm_out_file,
-	       "\t# Section names in %s are offsets into this table\n",
-	       LTO_SEGMENT_NAME);
-      fprintf (asm_out_file, "%s\n", lto_section_names);
+      FOR_EACH_VEC_ELT (darwin_lto_section_e, lto_section_names, count, ref)
+	{
+	  fprintf (asm_out_file, "L_GNU_LTO_NAME%d:\n", count);
+         /* We have to jump through hoops to get the values of the intra-section
+            offsets... */
+	  fprintf (asm_out_file, 
+		   "\t.set L$gnu$lto$noff%d,L_GNU_LTO_NAME%d-L_GNU_LTO_NAME0\n",
+		   count, count);
+	  fprintf (asm_out_file,
+		   "\t.set L$gnu$lto$nsiz%d,L_GNU_LTO_NAME%d-L_GNU_LTO_NAME%d\n",
+		   count, count+1, count);
+	  fprintf (asm_out_file, "\t.asciz\t\"%s\"\n", ref->sectname);
+	}
+      fprintf (asm_out_file, "L_GNU_LTO_NAME%d:\t;# end\n", lto_section_num);
+      /* make sure our termination label stays in this section.  */
+      fputs ("\t.space\t1\n", asm_out_file);
+
+      /* Emit the Index. */
+      fprintf (asm_out_file, "\t.section %s,%s,regular,debug\n",
+	       LTO_SEGMENT_NAME, LTO_INDEX_SECTION);
+      fputs ("\t.align\t2\n", asm_out_file);
+      fputs ("# Section offset, Section length, Name offset, Name length\n", asm_out_file);
+      FOR_EACH_VEC_ELT (darwin_lto_section_e, lto_section_names, count, ref)
+	{
+	  fprintf (asm_out_file, "%s L$gnu$lto$offs%d\t;# %s\n",
+		   op, count, ref->sectname);
+	  fprintf (asm_out_file, "%s L$gnu$lto$size%d\n", op, count);
+	  fprintf (asm_out_file, "%s L$gnu$lto$noff%d\n", op, count);
+	  fprintf (asm_out_file, "%s L$gnu$lto$nsiz%d\n", op, count);
+	}
     }
-  obstack_free (&lto_section_names_obstack, NULL);
 
   /* If we have section anchors, then we must prevent the linker from
      re-arranging data.  */
Index: include/simple-object.h
===================================================================
--- include/simple-object.h	(revision 179332)
+++ include/simple-object.h	(working copy)
@@ -45,12 +45,13 @@ typedef struct simple_object_read_struct simple_ob
    descriptor, and OFFSET, an offset within the file.  The offset is
    for use with archives, and should be 0 for an ordinary object file.
    The descriptor must remain open until done with the returned
-   simple_object_read.  SEGMENT_NAME is used on Mach-O and is required
-   on that platform: it means to only look at sections within the
-   segment with that name.  It is ignored for other object file
-   formats.  On error, this function returns NULL, and sets *ERRMSG to
-   an error string and sets *ERR to an errno value or 0 if there is no
-   relevant errno.  */
+   simple_object_read.  SEGMENT_NAME is used to provide a GNU extension
+   to Mach-O.  When provided, only sections within that segment will be
+   considered and, additionally, they will be processed according to a GNU
+   extension to Mach-O.  SEGMENT_NAME is ignored for other object file
+   formats.  On error, this function returns NULL, and sets *ERRMSG to an
+   error string and sets *ERR to an errno value or 0 if there is no relevant
+   errno.  */
 
 extern simple_object_read *
 simple_object_start_read (int descriptor, off_t offset,
@@ -139,12 +140,12 @@ typedef struct simple_object_write_struct simple_o
 /* Start creating a new object file which is like ATTRS.  You must
    fetch attribute information from an existing object file before you
    can create a new one.  There is currently no support for creating
-   an object file de novo.  The segment name is only used on Mach-O,
-   where it is required.  It means that all sections are created
-   within that segment.  It is ignored for other object file formats.
-   On error this function returns NULL, sets *ERRMSG to an error
-   message, and sets *ERR to an errno value or 0 if there isn't
-   one.  */
+   an object file de novo.  SEGMENT_NAME is only used on Mach-O and, if
+   set, it will cause all sections supplied to be stored according to a
+   GNU section wrapper extension to the format.  It is ignored for other
+   object file formats.  On error this function returns NULL, sets *ERRMSG
+   to an error message, and sets *ERR to an errno value or 0 if there
+   isn't one.  */
 
 extern simple_object_write *
 simple_object_start_write (simple_object_attributes *attrs,
