From 85d7ed08a86f70ad36cb89001c825e25deafc2a2 Mon Sep 17 00:00:00 2001 From: DJ Delorie Date: Wed, 26 Oct 2011 15:56:36 +0000 Subject: merge from gcc --- libiberty/simple-object-mach-o.c | 515 +++++++++++++++++++++++++++++++++------ 1 file changed, 435 insertions(+), 80 deletions(-) (limited to 'libiberty/simple-object-mach-o.c') diff --git a/libiberty/simple-object-mach-o.c b/libiberty/simple-object-mach-o.c index bbbbd09..af5e4f9 100644 --- a/libiberty/simple-object-mach-o.c +++ b/libiberty/simple-object-mach-o.c @@ -1,5 +1,5 @@ /* simple-object-mach-o.c -- routines to manipulate Mach-O object files. - Copyright 2010 Free Software Foundation, Inc. + Copyright 2010, 2011 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. This program is free software; you can redistribute it and/or modify it @@ -174,6 +174,15 @@ struct mach_o_section_64 #define GNU_SECTION_NAMES "__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_WRAPPER_SECTS "__wrapper_sects" +#define GNU_WRAPPER_INDEX "__wrapper_index" +#define GNU_WRAPPER_NAMES "__wrapper_names" + /* Private data for an simple_object_read. */ struct simple_object_mach_o_read @@ -214,7 +223,18 @@ 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 ( @@ -356,8 +376,29 @@ simple_object_mach_o_section_info (int is_big_endian, int is_32, } } -/* 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 gnu extensions to mach-o: + + 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. + + We will also allow an extension that permits long names (more than 16 + characters) to be used with mach-o. In this case, the section name has + a specific format embedding an index into a name table, and the file must + contain such name table. + + Return 1 if we should continue, 0 if the caller should return. */ + +#define SOMO_SECTS_PRESENT 0x01 +#define SOMO_INDEX_PRESENT 0x02 +#define SOMO_NAMES_PRESENT 0x04 +#define SOMO_LONGN_PRESENT 0x08 +#define SOMO_WRAPPING (SOMO_SECTS_PRESENT | SOMO_INDEX_PRESENT \ + | SOMO_NAMES_PRESENT) static int simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, @@ -378,9 +419,20 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, unsigned int nsects; unsigned char *secdata; unsigned int i; + unsigned int gnu_sections_found; unsigned int strtab_index; + unsigned int index_index; + unsigned int nametab_index; + unsigned int sections_index; char *strtab; + char *nametab; + unsigned char *index; size_t strtab_size; + size_t nametab_size; + size_t index_size; + unsigned int n_wrapped_sects; + size_t wrapper_sect_size; + off_t wrapper_sect_offset; fetch_32 = (omr->is_big_endian ? simple_object_fetch_big_32 @@ -409,6 +461,8 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, 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,9 +471,13 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, return 0; } - /* Scan for a __section_names section. This is in effect a GNU - extension that permits section names longer than 16 chars. */ + /* Scan for special sections that signal GNU extensions to the format. */ + gnu_sections_found = 0; + index_index = nsects; + sections_index = nsects; + strtab_index = nsects; + nametab_index = nsects; for (i = 0; i < nsects; ++i) { size_t nameoff; @@ -427,19 +485,104 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t 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; + if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0) + { + nametab_index = i; + gnu_sections_found |= SOMO_NAMES_PRESENT; + } + else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0) + { + index_index = i; + gnu_sections_found |= SOMO_INDEX_PRESENT; + } + else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0) + { + sections_index = i; + gnu_sections_found |= SOMO_SECTS_PRESENT; + } + else if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0) + { + strtab_index = i; + gnu_sections_found |= SOMO_LONGN_PRESENT; + } } - strtab_index = i; - if (strtab_index >= nsects) + /* If any of the special wrapper section components is present, then + they all should be. */ + + if ((gnu_sections_found & SOMO_WRAPPING) != 0) { - strtab = NULL; - strtab_size = 0; + off_t nametab_offset; + off_t index_offset; + + if ((gnu_sections_found & SOMO_WRAPPING) != SOMO_WRAPPING) + { + *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 + nametab_index * sechdrsize, + &nametab_offset, &nametab_size); + nametab = XNEWVEC (char, nametab_size); + if (!simple_object_internal_read (sobj->descriptor, + sobj->offset + nametab_offset, + (unsigned char *) nametab, nametab_size, + errmsg, err)) + { + XDELETEVEC (nametab); + 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 (index); + XDELETEVEC (nametab); + XDELETEVEC (secdata); + return 0; + } + + /* The index contains 4 unsigned ints 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 { + index = NULL; + index_size = 0; + nametab = NULL; + nametab_size = 0; + n_wrapped_sects = 0; + } + + /* If we have a long names section, fetch it. */ + + if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) + { off_t strtab_offset; simple_object_mach_o_section_info (omr->is_big_endian, is_32, @@ -452,52 +595,120 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, errmsg, err)) { XDELETEVEC (strtab); + XDELETEVEC (index); + XDELETEVEC (nametab); XDELETEVEC (secdata); return 0; } } + else + { + strtab = NULL; + strtab_size = 0; + strtab_index = nsects; + } /* 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]; char *name; off_t secoffset; size_t secsize; + int l; - if (i == strtab_index) + sechdr = secdata + i * sechdrsize; + + /* We've already processed the long section names. */ + + if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0 + && i == strtab_index) continue; - sechdr = secdata + i * sechdrsize; + /* We only act on the segment named. */ 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'; + /* Process sections associated with the wrapper. */ - name = &namebuf[0]; - if (strtab != NULL && name[0] == '_' && name[1] == '_') + if ((gnu_sections_found & SOMO_WRAPPING) != 0) { - unsigned long stringoffset; + if (i == nametab_index || i == index_index) + continue; - if (sscanf (name + 2, "%08lX", &stringoffset) == 1) + if (i == sections_index) { - if (stringoffset >= strtab_size) + unsigned 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 = nametab + name_offset; + + if (!(*pfn) (data, name, secoffset, secsize)) + { + *errmsg = NULL; + *err = 0; + XDELETEVEC (index); + XDELETEVEC (nametab); + XDELETEVEC (strtab); + XDELETEVEC (secdata); + return 0; + } } - - name = strtab + stringoffset; + continue; } } + if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) + { + 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 (stringoffset >= strtab_size) + { + *errmsg = "section name offset out of range"; + *err = 0; + XDELETEVEC (index); + XDELETEVEC (nametab); + XDELETEVEC (strtab); + XDELETEVEC (secdata); + return 0; + } + + name = strtab + stringoffset; + } + } + } + else + { + /* Otherwise, make a name like __segment,__section as per the + convention in mach-o asm. */ + name = &namebuf[0]; + 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 + l + 1, (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 +716,16 @@ simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, { *errmsg = NULL; *err = 0; + XDELETEVEC (index); + XDELETEVEC (nametab); XDELETEVEC (strtab); XDELETEVEC (secdata); return 0; } } + XDELETEVEC (index); + XDELETEVEC (nametab); XDELETEVEC (strtab); XDELETEVEC (secdata); @@ -724,9 +939,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 +963,7 @@ simple_object_mach_o_write_section_header (simple_object_write *sobj, 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 +988,7 @@ simple_object_mach_o_write_section_header (simple_object_write *sobj, 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 +1008,25 @@ simple_object_mach_o_write_section_header (simple_object_write *sobj, 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 wrapper extension is not in force, 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 +1043,10 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, simple_object_write_section *section; unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)]; unsigned char *hdr; + size_t nsects_in; + unsigned int *index; + char *snames; + unsigned int sect; set_32 = (attrs->is_big_endian ? simple_object_set_big_32 @@ -834,19 +1067,62 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, 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 (sobj->segment_name != NULL) + { + /* 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 32 bit int 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 (unsigned int, 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; + index = NULL; + snames = NULL; + } + 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 +1153,126 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, secsize += buffer->size; } - snprintf (namebuf, sizeof namebuf, "__%08X", name_offset); + if (sobj->segment_name != NULL) + { + index[sect*4+0] = (unsigned int) 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 != NULL) + { + int len = comma - section->name; + len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : 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 (sobj->segment_name != NULL) + { + size_t secsize; + unsigned 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], + 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. */ + /* 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_SECTION_NAMES, secaddr, - name_offset, offset, 0, - errmsg, err)) - return 0; + 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; - for (section = sobj->sections; section != NULL; section = section->next) - { - size_t namelen; + /* ... and the content.. */ + if (!simple_object_internal_write (descriptor, offset, + (const unsigned char *) snames, + name_offset, errmsg, err)) + return 0; + + sechdr_offset += sechdrsize; + secaddr += name_offset; + offset += name_offset; + + /* 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; - namelen = strlen (section->name) + 1; + /* ... 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 +1286,8 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, 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 +1297,7 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, /* 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 +1313,8 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, 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 +1324,7 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, /* 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 +1339,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; } -- cgit v1.1