12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379 |
- /* simple-object-mach-o.c -- routines to manipulate Mach-O object files.
- Copyright 2010, 2011, 2013 Free Software Foundation, Inc.
- Written by Ian Lance Taylor, Google.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 51 Franklin Street - Fifth Floor,
- Boston, MA 02110-1301, USA. */
- #include "config.h"
- #include "libiberty.h"
- #include "simple-object.h"
- #include <stddef.h>
- #ifdef HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #ifdef HAVE_STDINT_H
- #include <stdint.h>
- #endif
- #ifdef HAVE_STRING_H
- #include <string.h>
- #endif
- #ifdef HAVE_INTTYPES_H
- #include <inttypes.h>
- #endif
- #include "simple-object-common.h"
- /* Mach-O structures and constants. */
- /* Mach-O header (32-bit version). */
- struct mach_o_header_32
- {
- unsigned char magic[4]; /* Magic number. */
- unsigned char cputype[4]; /* CPU that this object is for. */
- unsigned char cpusubtype[4]; /* CPU subtype. */
- unsigned char filetype[4]; /* Type of file. */
- unsigned char ncmds[4]; /* Number of load commands. */
- unsigned char sizeofcmds[4]; /* Total size of load commands. */
- unsigned char flags[4]; /* Flags for special featues. */
- };
- /* Mach-O header (64-bit version). */
- struct mach_o_header_64
- {
- unsigned char magic[4]; /* Magic number. */
- unsigned char cputype[4]; /* CPU that this object is for. */
- unsigned char cpusubtype[4]; /* CPU subtype. */
- unsigned char filetype[4]; /* Type of file. */
- unsigned char ncmds[4]; /* Number of load commands. */
- unsigned char sizeofcmds[4]; /* Total size of load commands. */
- unsigned char flags[4]; /* Flags for special featues. */
- unsigned char reserved[4]; /* Reserved. Duh. */
- };
- /* For magic field in header. */
- #define MACH_O_MH_MAGIC 0xfeedface
- #define MACH_O_MH_MAGIC_64 0xfeedfacf
- /* For filetype field in header. */
- #define MACH_O_MH_OBJECT 0x01
- /* A Mach-O file is a list of load commands. This is the header of a
- load command. */
- struct mach_o_load_command
- {
- unsigned char cmd[4]; /* The type of load command. */
- unsigned char cmdsize[4]; /* Size in bytes of entire command. */
- };
- /* For cmd field in load command. */
- #define MACH_O_LC_SEGMENT 0x01
- #define MACH_O_LC_SEGMENT_64 0x19
- /* LC_SEGMENT load command. */
- struct mach_o_segment_command_32
- {
- unsigned char cmd[4]; /* The type of load command (LC_SEGMENT). */
- unsigned char cmdsize[4]; /* Size in bytes of entire command. */
- unsigned char segname[16]; /* Name of this segment. */
- unsigned char vmaddr[4]; /* Virtual memory address of this segment. */
- unsigned char vmsize[4]; /* Size there, in bytes. */
- unsigned char fileoff[4]; /* Offset in bytes of the data to be mapped. */
- unsigned char filesize[4]; /* Size in bytes on disk. */
- unsigned char maxprot[4]; /* Maximum permitted vmem protection. */
- unsigned char initprot[4]; /* Initial vmem protection. */
- unsigned char nsects[4]; /* Number of sections in this segment. */
- unsigned char flags[4]; /* Flags that affect the loading. */
- };
- /* LC_SEGMENT_64 load command. */
- struct mach_o_segment_command_64
- {
- unsigned char cmd[4]; /* The type of load command (LC_SEGMENT_64). */
- unsigned char cmdsize[4]; /* Size in bytes of entire command. */
- unsigned char segname[16]; /* Name of this segment. */
- unsigned char vmaddr[8]; /* Virtual memory address of this segment. */
- unsigned char vmsize[8]; /* Size there, in bytes. */
- unsigned char fileoff[8]; /* Offset in bytes of the data to be mapped. */
- unsigned char filesize[8]; /* Size in bytes on disk. */
- unsigned char maxprot[4]; /* Maximum permitted vmem protection. */
- unsigned char initprot[4]; /* Initial vmem protection. */
- unsigned char nsects[4]; /* Number of sections in this segment. */
- unsigned char flags[4]; /* Flags that affect the loading. */
- };
- /* 32-bit section header. */
- struct mach_o_section_32
- {
- unsigned char sectname[16]; /* Section name. */
- unsigned char segname[16]; /* Segment that the section belongs to. */
- unsigned char addr[4]; /* Address of this section in memory. */
- unsigned char size[4]; /* Size in bytes of this section. */
- unsigned char offset[4]; /* File offset of this section. */
- unsigned char align[4]; /* log2 of this section's alignment. */
- unsigned char reloff[4]; /* File offset of this section's relocs. */
- unsigned char nreloc[4]; /* Number of relocs for this section. */
- unsigned char flags[4]; /* Section flags/attributes. */
- unsigned char reserved1[4];
- unsigned char reserved2[4];
- };
- /* 64-bit section header. */
- struct mach_o_section_64
- {
- unsigned char sectname[16]; /* Section name. */
- unsigned char segname[16]; /* Segment that the section belongs to. */
- unsigned char addr[8]; /* Address of this section in memory. */
- unsigned char size[8]; /* Size in bytes of this section. */
- unsigned char offset[4]; /* File offset of this section. */
- unsigned char align[4]; /* log2 of this section's alignment. */
- unsigned char reloff[4]; /* File offset of this section's relocs. */
- unsigned char nreloc[4]; /* Number of relocs for this section. */
- unsigned char flags[4]; /* Section flags/attributes. */
- unsigned char reserved1[4];
- unsigned char reserved2[4];
- unsigned char reserved3[4];
- };
- /* Flags for Mach-O sections. */
- #define MACH_O_S_ATTR_DEBUG 0x02000000
- /* The length of a segment or section name. */
- #define MACH_O_NAME_LEN (16)
- /* A GNU specific extension for long section names. */
- #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
- {
- /* User specified segment name. */
- char *segment_name;
- /* Magic number. */
- unsigned int magic;
- /* Whether this file is big-endian. */
- int is_big_endian;
- /* CPU type from header. */
- unsigned int cputype;
- /* CPU subtype from header. */
- unsigned int cpusubtype;
- /* Number of commands, from header. */
- unsigned int ncmds;
- /* Flags from header. */
- unsigned int flags;
- /* Reserved field from header, only used on 64-bit. */
- unsigned int reserved;
- };
- /* Private data for an simple_object_attributes. */
- struct simple_object_mach_o_attributes
- {
- /* Magic number. */
- unsigned int magic;
- /* Whether this file is big-endian. */
- int is_big_endian;
- /* CPU type from header. */
- unsigned int cputype;
- /* CPU subtype from header. */
- unsigned int cpusubtype;
- /* Flags from header. */
- unsigned int flags;
- /* Reserved field from header, only used on 64-bit. */
- unsigned int reserved;
- };
- /* 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],
- int descriptor,
- off_t offset,
- const char *segment_name,
- const char **errmsg,
- int *err)
- {
- unsigned int magic;
- int is_big_endian;
- unsigned int (*fetch_32) (const unsigned char *);
- unsigned int filetype;
- struct simple_object_mach_o_read *omr;
- unsigned char buf[sizeof (struct mach_o_header_64)];
- unsigned char *b;
- magic = simple_object_fetch_big_32 (header);
- if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
- is_big_endian = 1;
- else
- {
- magic = simple_object_fetch_little_32 (header);
- if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
- is_big_endian = 0;
- else
- {
- *errmsg = NULL;
- *err = 0;
- return NULL;
- }
- }
- #ifndef UNSIGNED_64BIT_TYPE
- if (magic == MACH_O_MH_MAGIC_64)
- {
- *errmsg = "64-bit Mach-O objects not supported";
- *err = 0;
- return NULL;
- }
- #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)
- {
- *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;
- }
- /* The 32-bit and 64-bit headers are similar enough that we can use
- the same code. */
- fetch_32 = (is_big_endian
- ? simple_object_fetch_big_32
- : simple_object_fetch_little_32);
- if (!simple_object_internal_read (descriptor, offset, buf,
- (magic == MACH_O_MH_MAGIC
- ? sizeof (struct mach_o_header_32)
- : sizeof (struct mach_o_header_64)),
- errmsg, err))
- return NULL;
- b = &buf[0];
- 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";
- *err = 0;
- return NULL;
- }
- omr = XNEW (struct simple_object_mach_o_read);
- 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));
- omr->cpusubtype = (*fetch_32) (b
- + offsetof (struct mach_o_header_32,
- cpusubtype));
- omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds));
- omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags));
- if (magic == MACH_O_MH_MAGIC)
- omr->reserved = 0;
- else
- omr->reserved = (*fetch_32) (b
- + offsetof (struct mach_o_header_64,
- reserved));
- return (void *) omr;
- }
- /* Get the file offset and size from a section header. */
- static void
- simple_object_mach_o_section_info (int is_big_endian, int is_32,
- const unsigned char *sechdr, off_t *offset,
- size_t *size)
- {
- unsigned int (*fetch_32) (const unsigned char *);
- ulong_type (*fetch_64) (const unsigned char *);
- fetch_32 = (is_big_endian
- ? simple_object_fetch_big_32
- : simple_object_fetch_little_32);
- fetch_64 = NULL;
- #ifdef UNSIGNED_64BIT_TYPE
- fetch_64 = (is_big_endian
- ? simple_object_fetch_big_64
- : simple_object_fetch_little_64);
- #endif
- if (is_32)
- {
- *offset = fetch_32 (sechdr
- + offsetof (struct mach_o_section_32, offset));
- *size = fetch_32 (sechdr
- + offsetof (struct mach_o_section_32, size));
- }
- else
- {
- *offset = fetch_32 (sechdr
- + offsetof (struct mach_o_section_64, offset));
- *size = fetch_64 (sechdr
- + offsetof (struct mach_o_section_64, size));
- }
- }
- /* 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,
- const unsigned char *segbuf,
- int (*pfn) (void *, const char *, off_t offset,
- off_t length),
- void *data,
- const char **errmsg, int *err)
- {
- struct simple_object_mach_o_read *omr =
- (struct simple_object_mach_o_read *) sobj->data;
- unsigned int (*fetch_32) (const unsigned char *);
- int is_32;
- size_t seghdrsize;
- size_t sechdrsize;
- size_t segname_offset;
- size_t sectname_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 = 0;
- fetch_32 = (omr->is_big_endian
- ? simple_object_fetch_big_32
- : simple_object_fetch_little_32);
- is_32 = omr->magic == MACH_O_MH_MAGIC;
- if (is_32)
- {
- seghdrsize = sizeof (struct mach_o_segment_command_32);
- sechdrsize = sizeof (struct mach_o_section_32);
- segname_offset = offsetof (struct mach_o_section_32, segname);
- sectname_offset = offsetof (struct mach_o_section_32, sectname);
- nsects = (*fetch_32) (segbuf
- + offsetof (struct mach_o_segment_command_32,
- nsects));
- }
- else
- {
- seghdrsize = sizeof (struct mach_o_segment_command_64);
- sechdrsize = sizeof (struct mach_o_section_64);
- segname_offset = offsetof (struct mach_o_section_64, segname);
- sectname_offset = offsetof (struct mach_o_section_64, sectname);
- nsects = (*fetch_32) (segbuf
- + offsetof (struct mach_o_segment_command_64,
- 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))
- {
- XDELETEVEC (secdata);
- return 0;
- }
- /* 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;
- 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_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;
- }
- }
- /* If any of the special wrapper section components is present, then
- they all should be. */
- if ((gnu_sections_found & SOMO_WRAPPING) != 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,
- 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 (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 * 2 + 2];
- char *name;
- off_t secoffset;
- size_t secsize;
- int l;
- sechdr = secdata + i * sechdrsize;
- /* We've already processed the long section names. */
- if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0
- && i == strtab_index)
- continue;
- /* We only act on the segment named. */
- if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0)
- continue;
- /* Process sections associated with the wrapper. */
- if ((gnu_sections_found & SOMO_WRAPPING) != 0)
- {
- if (i == nametab_index || i == index_index)
- continue;
- if (i == sections_index)
- {
- unsigned int j;
- for (j = 0; j < n_wrapped_sects; ++j)
- {
- 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;
- }
- }
- 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];
- memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN);
- namebuf[MACH_O_NAME_LEN] = '\0';
- l = strlen (namebuf);
- namebuf[l] = ',';
- memcpy (namebuf + l + 1, (char *) sechdr + sectname_offset,
- MACH_O_NAME_LEN);
- namebuf[l + 1 + MACH_O_NAME_LEN] = '\0';
- }
- simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr,
- &secoffset, &secsize);
- if (!(*pfn) (data, name, secoffset, secsize))
- {
- *errmsg = NULL;
- *err = 0;
- XDELETEVEC (index);
- XDELETEVEC (nametab);
- XDELETEVEC (strtab);
- XDELETEVEC (secdata);
- return 0;
- }
- }
- XDELETEVEC (index);
- XDELETEVEC (nametab);
- XDELETEVEC (strtab);
- XDELETEVEC (secdata);
- return 1;
- }
- /* Find all sections in a Mach-O file. */
- static const char *
- simple_object_mach_o_find_sections (simple_object_read *sobj,
- int (*pfn) (void *, const char *,
- off_t offset, off_t length),
- void *data,
- int *err)
- {
- struct simple_object_mach_o_read *omr =
- (struct simple_object_mach_o_read *) sobj->data;
- off_t offset;
- size_t seghdrsize;
- unsigned int (*fetch_32) (const unsigned char *);
- const char *errmsg;
- unsigned int i;
- if (omr->magic == MACH_O_MH_MAGIC)
- {
- offset = sizeof (struct mach_o_header_32);
- seghdrsize = sizeof (struct mach_o_segment_command_32);
- }
- else
- {
- offset = sizeof (struct mach_o_header_64);
- seghdrsize = sizeof (struct mach_o_segment_command_64);
- }
- fetch_32 = (omr->is_big_endian
- ? simple_object_fetch_big_32
- : simple_object_fetch_little_32);
- for (i = 0; i < omr->ncmds; ++i)
- {
- unsigned char loadbuf[sizeof (struct mach_o_load_command)];
- unsigned int cmd;
- unsigned int cmdsize;
- if (!simple_object_internal_read (sobj->descriptor,
- sobj->offset + offset,
- loadbuf,
- sizeof (struct mach_o_load_command),
- &errmsg, err))
- return errmsg;
- cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd));
- cmdsize = (*fetch_32) (loadbuf
- + offsetof (struct mach_o_load_command, cmdsize));
- if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64)
- {
- unsigned char segbuf[sizeof (struct mach_o_segment_command_64)];
- int r;
- if (!simple_object_internal_read (sobj->descriptor,
- sobj->offset + offset,
- segbuf, seghdrsize, &errmsg, err))
- return errmsg;
- r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn,
- data, &errmsg, err);
- if (!r)
- return errmsg;
- }
- offset += cmdsize;
- }
- return NULL;
- }
- /* Fetch the attributes for an simple_object_read. */
- static void *
- simple_object_mach_o_fetch_attributes (simple_object_read *sobj,
- const char **errmsg ATTRIBUTE_UNUSED,
- int *err ATTRIBUTE_UNUSED)
- {
- struct simple_object_mach_o_read *omr =
- (struct simple_object_mach_o_read *) sobj->data;
- struct simple_object_mach_o_attributes *ret;
- ret = XNEW (struct simple_object_mach_o_attributes);
- ret->magic = omr->magic;
- ret->is_big_endian = omr->is_big_endian;
- ret->cputype = omr->cputype;
- ret->cpusubtype = omr->cpusubtype;
- ret->flags = omr->flags;
- ret->reserved = omr->reserved;
- return ret;
- }
- /* Release the private data for an simple_object_read. */
- static void
- simple_object_mach_o_release_read (void *data)
- {
- struct simple_object_mach_o_read *omr =
- (struct simple_object_mach_o_read *) data;
- free (omr->segment_name);
- XDELETE (omr);
- }
- /* Compare two attributes structures. */
- static const char *
- simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err)
- {
- struct simple_object_mach_o_attributes *to =
- (struct simple_object_mach_o_attributes *) todata;
- struct simple_object_mach_o_attributes *from =
- (struct simple_object_mach_o_attributes *) fromdata;
- if (to->magic != from->magic
- || to->is_big_endian != from->is_big_endian
- || to->cputype != from->cputype)
- {
- *err = 0;
- return "Mach-O object format mismatch";
- }
- return NULL;
- }
- /* Release the private data for an attributes structure. */
- static void
- simple_object_mach_o_release_attributes (void *data)
- {
- XDELETE (data);
- }
- /* Prepare to write out a file. */
- static void *
- simple_object_mach_o_start_write (void *attributes_data,
- const char **errmsg ATTRIBUTE_UNUSED,
- int *err ATTRIBUTE_UNUSED)
- {
- struct simple_object_mach_o_attributes *attrs =
- (struct simple_object_mach_o_attributes *) attributes_data;
- struct simple_object_mach_o_attributes *ret;
- /* We're just going to record the attributes, but we need to make a
- copy because the user may delete them. */
- ret = XNEW (struct simple_object_mach_o_attributes);
- *ret = *attrs;
- return ret;
- }
- /* Write out the header of a Mach-O file. */
- static int
- simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor,
- size_t nsects, const char **errmsg,
- int *err)
- {
- struct simple_object_mach_o_attributes *attrs =
- (struct simple_object_mach_o_attributes *) sobj->data;
- void (*set_32) (unsigned char *, unsigned int);
- unsigned char hdrbuf[sizeof (struct mach_o_header_64)];
- unsigned char *hdr;
- size_t wrsize;
- set_32 = (attrs->is_big_endian
- ? simple_object_set_big_32
- : simple_object_set_little_32);
- memset (hdrbuf, 0, sizeof hdrbuf);
- /* The 32-bit and 64-bit headers start out the same. */
- hdr = &hdrbuf[0];
- set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic);
- set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype);
- set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype),
- attrs->cpusubtype);
- set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT);
- set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1);
- set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags);
- if (attrs->magic == MACH_O_MH_MAGIC)
- {
- wrsize = sizeof (struct mach_o_header_32);
- set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds),
- (sizeof (struct mach_o_segment_command_32)
- + nsects * sizeof (struct mach_o_section_32)));
- }
- else
- {
- set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds),
- (sizeof (struct mach_o_segment_command_64)
- + nsects * sizeof (struct mach_o_section_64)));
- set_32 (hdr + offsetof (struct mach_o_header_64, reserved),
- attrs->reserved);
- wrsize = sizeof (struct mach_o_header_64);
- }
- return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize,
- errmsg, err);
- }
- /* Write a Mach-O section header. */
- static int
- simple_object_mach_o_write_section_header (simple_object_write *sobj,
- int descriptor,
- size_t sechdr_offset,
- 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 =
- (struct simple_object_mach_o_attributes *) sobj->data;
- void (*set_32) (unsigned char *, unsigned int);
- unsigned char hdrbuf[sizeof (struct mach_o_section_64)];
- unsigned char *hdr;
- size_t sechdrsize;
- set_32 = (attrs->is_big_endian
- ? simple_object_set_big_32
- : simple_object_set_little_32);
- memset (hdrbuf, 0, sizeof hdrbuf);
- hdr = &hdrbuf[0];
- if (attrs->magic == MACH_O_MH_MAGIC)
- {
- 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),
- 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);
- set_32 (hdr + offsetof (struct mach_o_section_32, align), align);
- /* reloff left as zero. */
- /* nreloc left as zero. */
- set_32 (hdr + offsetof (struct mach_o_section_32, flags),
- MACH_O_S_ATTR_DEBUG);
- /* reserved1 left as zero. */
- /* reserved2 left as zero. */
- sechdrsize = sizeof (struct mach_o_section_32);
- }
- else
- {
- #ifdef UNSIGNED_64BIT_TYPE
- void (*set_64) (unsigned char *, ulong_type);
- set_64 = (attrs->is_big_endian
- ? simple_object_set_big_64
- : simple_object_set_little_64);
- 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),
- 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);
- set_32 (hdr + offsetof (struct mach_o_section_64, align), align);
- /* reloff left as zero. */
- /* nreloc left as zero. */
- set_32 (hdr + offsetof (struct mach_o_section_64, flags),
- MACH_O_S_ATTR_DEBUG);
- /* reserved1 left as zero. */
- /* reserved2 left as zero. */
- /* reserved3 left as zero. */
- #endif
- sechdrsize = sizeof (struct mach_o_section_64);
- }
- return simple_object_internal_write (descriptor, sechdr_offset, hdr,
- sechdrsize, errmsg, err);
- }
- /* 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,
- int *err)
- {
- struct simple_object_mach_o_attributes *attrs =
- (struct simple_object_mach_o_attributes *) sobj->data;
- void (*set_32) (unsigned char *, unsigned int);
- size_t hdrsize;
- size_t seghdrsize;
- size_t sechdrsize;
- size_t cmdsize;
- size_t offset;
- size_t sechdr_offset;
- size_t secaddr;
- unsigned int name_offset;
- 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
- : simple_object_set_little_32);
- /* Write out the sections first. */
- if (attrs->magic == MACH_O_MH_MAGIC)
- {
- hdrsize = sizeof (struct mach_o_header_32);
- seghdrsize = sizeof (struct mach_o_segment_command_32);
- sechdrsize = sizeof (struct mach_o_section_32);
- }
- else
- {
- hdrsize = sizeof (struct mach_o_header_64);
- seghdrsize = sizeof (struct mach_o_segment_command_64);
- 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;
- offset = hdrsize + cmdsize;
- secaddr = 0;
- 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;
- mask = (1U << section->align) - 1;
- new_offset = offset + mask;
- new_offset &= ~ mask;
- while (new_offset > offset)
- {
- unsigned char zeroes[16];
- size_t write;
- memset (zeroes, 0, sizeof zeroes);
- write = new_offset - offset;
- if (write > sizeof zeroes)
- write = sizeof zeroes;
- if (!simple_object_internal_write (descriptor, offset, zeroes, write,
- errmsg, err))
- return 0;
- offset += write;
- }
- secsize = 0;
- for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
- {
- if (!simple_object_internal_write (descriptor, offset + secsize,
- ((const unsigned char *)
- buffer->buffer),
- buffer->size, errmsg, err))
- return 0;
- secsize += buffer->size;
- }
- 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,
- 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;
- /* 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;
- /* ... 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;
- /* ... and the content.. */
- if (!simple_object_internal_write (descriptor, offset,
- (const unsigned char *) index,
- nsects_in*16, errmsg, err))
- return 0;
- XDELETEVEC (index);
- XDELETEVEC (snames);
- }
- /* Write out the segment header. */
- memset (hdrbuf, 0, sizeof hdrbuf);
- hdr = &hdrbuf[0];
- if (attrs->magic == MACH_O_MH_MAGIC)
- {
- set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd),
- MACH_O_LC_SEGMENT);
- set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize),
- cmdsize);
- /* 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),
- hdrsize + cmdsize);
- set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize),
- offset - (hdrsize + cmdsize));
- /* maxprot left as zero. */
- /* initprot left as zero. */
- set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects),
- *nsects);
- /* flags left as zero. */
- }
- else
- {
- #ifdef UNSIGNED_64BIT_TYPE
- void (*set_64) (unsigned char *, ulong_type);
- set_64 = (attrs->is_big_endian
- ? simple_object_set_big_64
- : simple_object_set_little_64);
- set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd),
- MACH_O_LC_SEGMENT);
- set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize),
- cmdsize);
- /* 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),
- hdrsize + cmdsize);
- set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize),
- offset - (hdrsize + cmdsize));
- /* maxprot left as zero. */
- /* initprot left as zero. */
- set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects),
- *nsects);
- /* flags left as zero. */
- #endif
- }
- return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize,
- errmsg, err);
- }
- /* Write out a complete Mach-O file. */
- static const char *
- simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor,
- int *err)
- {
- size_t nsects = 0;
- const char *errmsg;
- 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;
- return NULL;
- }
- /* Release the private data for an simple_object_write structure. */
- static void
- simple_object_mach_o_release_write (void *data)
- {
- XDELETE (data);
- }
- /* The Mach-O functions. */
- const struct simple_object_functions simple_object_mach_o_functions =
- {
- simple_object_mach_o_match,
- simple_object_mach_o_find_sections,
- simple_object_mach_o_fetch_attributes,
- simple_object_mach_o_release_read,
- simple_object_mach_o_attributes_merge,
- simple_object_mach_o_release_attributes,
- simple_object_mach_o_start_write,
- simple_object_mach_o_write_to_file,
- simple_object_mach_o_release_write
- };
|