===================================================================
@@ -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;
}
===================================================================
@@ -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 (<o_section_names_obstack, '\t');
- obstack_grow (<o_section_names_obstack, ".ascii ", 7);
- obstack_1grow (<o_section_names_obstack, '"');
- obstack_grow (<o_section_names_obstack, name, strlen (name));
- obstack_grow (<o_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 (<o_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 (<o_section_names_obstack, '\0');
- lto_section_names = XOBFINISH (<o_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 (<o_section_names_obstack, NULL);
/* If we have section anchors, then we must prevent the linker from
re-arranging data. */
===================================================================
@@ -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,