diff options
-rw-r--r-- | output/outmacho.c | 578 |
1 files changed, 314 insertions, 264 deletions
diff --git a/output/outmacho.c b/output/outmacho.c index e013735..eee2ea8 100644 --- a/output/outmacho.c +++ b/output/outmacho.c @@ -131,6 +131,16 @@ static evalfunc evaluate; extern struct ofmt of_macho; +/* Global file information. This should be cleaned up into either + a structure or as function arguments. */ +unsigned long head_ncmds = 0; +unsigned long head_sizeofcmds = 0; +unsigned long seg_filesize = 0; +unsigned long seg_vmsize = 0; +unsigned long seg_nsects = 0; +unsigned long rel_padcnt = 0; + + #define xstrncpy(xdst, xsrc) \ memset(xdst, '\0', sizeof(xdst)); /* zero out whole buffer */ \ strncpy(xdst, xsrc, sizeof(xdst)); /* copy over string */ \ @@ -563,102 +573,11 @@ static const char *macho_stdmac[] = { NULL }; -static void macho_cleanup(int debuginfo) +/* Calculate some values we'll need for writing later. */ + +static void macho_calculate_sizes (void) { struct section *s; - struct reloc *r; - unsigned long offset, rel_padcnt = 0; - unsigned long head_ncmds = 0; - unsigned long head_sizeofcmds = 0; - unsigned long seg_nsects = 0; - unsigned long seg_filesize = 0; - unsigned long seg_vmsize = 0; - - (void)debuginfo; - - /* mach-o object file structure: - ** - ** mach header - ** ulong magic - ** int cpu type - ** int cpu subtype - ** ulong mach file type - ** ulong number of load commands - ** ulong size of all load commands - ** (includes section struct size of segment command) - ** ulong flags - ** - ** segment command - ** ulong command type == LC_SEGMENT - ** ulong size of load command - ** (including section load commands) - ** char[16] segment name - ** ulong in-memory offset - ** ulong in-memory size - ** ulong in-file offset to data area - ** ulong in-file size - ** (in-memory size excluding zerofill sections) - ** int maximum vm protection - ** int initial vm protection - ** ulong number of sections - ** ulong flags - ** - ** section commands - ** char[16] section name - ** char[16] segment name - ** ulong in-memory offset - ** ulong in-memory size - ** ulong in-file offset - ** ulong alignment - ** (irrelevant in MH_OBJECT) - ** ulong in-file offset of relocation entires - ** ulong number of relocations - ** ulong flags - ** ulong reserved - ** ulong reserved - ** - ** symbol table command - ** ulong command type == LC_SYMTAB - ** ulong size of load command - ** ulong symbol table offset - ** ulong number of symbol table entries - ** ulong string table offset - ** ulong string table size - ** - ** raw section data - ** - ** padding to long boundary - ** - ** relocation data (struct reloc) - ** long offset - ** uint data (symbolnum, pcrel, length, extern, type) - ** - ** symbol table data (struct nlist) - ** long string table entry number - ** uchar type - ** (extern, absolute, defined in section) - ** uchar section - ** (0 for global symbols, section number of definition (>= 1, <= - ** 254) for local symbols, size of variable for common symbols - ** [type == extern]) - ** short description - ** (for stab debugging format) - ** ulong value (i.e. file offset) of symbol or stab offset - ** - ** string table data - ** list of null-terminated strings - */ - - /* simplifications over native NeXTstep assembler: - ** - no support for dynamic linking/symbols/libraries, section differences - ** - no sorting of symbols and string table, normally its ordered like - ** - local symbols, defined external symbols, undefined external symbols - ** - strings for external symbols, strings for local symbols - ** we just dump them in the order given by the source file and nasm what - ** seems to work fine with the link editor - */ - - /* first calculate and finalise all needed values */ /* count sections and calculate in-memory and in-file offsets */ for (s = sects; s != NULL; s = s->next) { @@ -671,7 +590,7 @@ static void macho_cleanup(int debuginfo) } /* calculate size of all headers, load commands and sections to - ** get a pointer to the start of all the raw data */ + ** get a pointer to the start of all the raw data */ if (seg_nsects > 0) { ++head_ncmds; head_sizeofcmds += @@ -679,83 +598,293 @@ static void macho_cleanup(int debuginfo) } if (nsyms > 0) { - ++head_ncmds; - head_sizeofcmds += MACHO_SYMCMD_SIZE; + ++head_ncmds; + head_sizeofcmds += MACHO_SYMCMD_SIZE; } +} - /* emit the Mach-O header. */ - fwritelong(MH_MAGIC, machofp); /* magic */ - fwritelong(CPU_TYPE_I386, machofp); /* CPU type */ - fwritelong(CPU_SUBTYPE_I386_ALL, machofp); /* CPU subtype */ - fwritelong(MH_OBJECT, machofp); /* Mach-O file type */ - fwritelong(head_ncmds, machofp); /* number of load commands */ - fwritelong(head_sizeofcmds, machofp); /* size of load commands */ - fwritelong(0, machofp); /* no flags */ +/* Write out the header information for the file. */ - offset = MACHO_HEADER_SIZE + head_sizeofcmds; +static void macho_write_header (void) +{ + fwritelong(MH_MAGIC, machofp); /* magic */ + fwritelong(CPU_TYPE_I386, machofp); /* CPU type */ + fwritelong(CPU_SUBTYPE_I386_ALL, machofp); /* CPU subtype */ + fwritelong(MH_OBJECT, machofp); /* Mach-O file type */ + fwritelong(head_ncmds, machofp); /* number of load commands */ + fwritelong(head_sizeofcmds, machofp); /* size of load commands */ + fwritelong(0, machofp); /* no flags */ +} - /* emit the segment load command */ - if (seg_nsects > 0) { - unsigned long s_addr = 0; - unsigned long rel_base = alignlong(offset + seg_filesize); - unsigned long s_reloff = 0; - - fwritelong(LC_SEGMENT, machofp); /* cmd == LC_SEGMENT */ - - /* size of load command including section load commands */ - fwritelong(MACHO_SEGCMD_SIZE + seg_nsects * - MACHO_SECTCMD_SIZE, machofp); - - /* in an MH_OBJECT file all sections are in one unnamed (name - ** all zeros) segment */ - fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16, 1, machofp); - fwritelong(0, machofp); /* in-memory offset */ - fwritelong(seg_vmsize, machofp); /* in-memory size */ - fwritelong(offset, machofp); /* in-file offset to data */ - fwritelong(seg_filesize, machofp); /* in-file size */ - fwritelong(VM_PROT_DEFAULT, machofp); /* maximum vm protection */ - fwritelong(VM_PROT_DEFAULT, machofp); /* initial vm protection */ - fwritelong(seg_nsects, machofp); /* number of sections */ - fwritelong(0, machofp); /* no flags */ - - /* emit section headers */ - for (s = sects; s != NULL; s = s->next) { - fwrite(s->sectname, sizeof(s->sectname), 1, machofp); - fwrite(s->segname, sizeof(s->segname), 1, machofp); - fwritelong(s_addr, machofp); - fwritelong(s->size, machofp); - - /* dummy data for zerofill sections or proper values */ - if ((s->flags & SECTION_TYPE) != S_ZEROFILL) { - fwritelong(offset, machofp); - fwritelong(0, machofp); - /* To be compatible with cctools as we emit - a zero reloff if we have no relocations. */ - fwritelong(s->nreloc ? rel_base + s_reloff : 0, machofp); - fwritelong(s->nreloc, machofp); - - offset += s->size; - s_reloff += s->nreloc * MACHO_RELINFO_SIZE; - } else { - fwritelong(0, machofp); - fwritelong(0, machofp); - fwritelong(0, machofp); - fwritelong(0, machofp); - } +/* Write out the segment load command at offset. */ - fwritelong(s->flags, machofp); /* flags */ - fwritelong(0, machofp); /* reserved */ - fwritelong(0, machofp); /* reserved */ +static unsigned long macho_write_segment (unsigned long offset) +{ + unsigned long s_addr = 0; + unsigned long rel_base = alignlong (offset + seg_filesize); + unsigned long s_reloff = 0; + struct section *s; - s_addr += s->size; - } + fwritelong(LC_SEGMENT, machofp); /* cmd == LC_SEGMENT */ + + /* size of load command including section load commands */ + fwritelong(MACHO_SEGCMD_SIZE + seg_nsects * + MACHO_SECTCMD_SIZE, machofp); + + /* in an MH_OBJECT file all sections are in one unnamed (name + ** all zeros) segment */ + fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16, 1, machofp); + fwritelong(0, machofp); /* in-memory offset */ + fwritelong(seg_vmsize, machofp); /* in-memory size */ + fwritelong(offset, machofp); /* in-file offset to data */ + fwritelong(seg_filesize, machofp); /* in-file size */ + fwritelong(VM_PROT_DEFAULT, machofp); /* maximum vm protection */ + fwritelong(VM_PROT_DEFAULT, machofp); /* initial vm protection */ + fwritelong(seg_nsects, machofp); /* number of sections */ + fwritelong(0, machofp); /* no flags */ + + /* emit section headers */ + for (s = sects; s != NULL; s = s->next) { + fwrite(s->sectname, sizeof(s->sectname), 1, machofp); + fwrite(s->segname, sizeof(s->segname), 1, machofp); + fwritelong(s_addr, machofp); + fwritelong(s->size, machofp); + + /* dummy data for zerofill sections or proper values */ + if ((s->flags & SECTION_TYPE) != S_ZEROFILL) { + fwritelong(offset, machofp); + fwritelong(0, machofp); + /* To be compatible with cctools as we emit + a zero reloff if we have no relocations. */ + fwritelong(s->nreloc ? rel_base + s_reloff : 0, machofp); + fwritelong(s->nreloc, machofp); + + offset += s->size; + s_reloff += s->nreloc * MACHO_RELINFO_SIZE; + } else { + fwritelong(0, machofp); + fwritelong(0, machofp); + fwritelong(0, machofp); + fwritelong(0, machofp); + } + + fwritelong(s->flags, machofp); /* flags */ + fwritelong(0, machofp); /* reserved */ + fwritelong(0, machofp); /* reserved */ + + s_addr += s->size; + } - /* at this point offset has reached rel_base - alignment, - ** remember how much we'll have to pad and skip relocation - ** data */ - rel_padcnt = rel_base - offset; - offset = rel_base + s_reloff; - } else + rel_padcnt = rel_base - offset; + offset = rel_base + s_reloff; + + return offset; +} + +/* For a given chain of relocs r, write out the entire relocation + chain to the object file. */ + +static void macho_write_relocs (struct reloc *r) +{ + while (r) { + unsigned long word2; + + fwritelong(r->addr, machofp); /* reloc offset */ + + word2 = r->snum; + word2 |= r->pcrel << 24; + word2 |= r->length << 25; + word2 |= r->ext << 27; + word2 |= r->type << 28; + fwritelong(word2, machofp); /* reloc data */ + + r = r->next; + } +} + +/* Write out the section data. */ +static void macho_write_section (void) +{ + struct section *s, *s2; + struct reloc *r; + char *rel_paddata = "\0\0\0"; + unsigned char fi, *p, *q, blk[4]; + long l; + + for (s = sects; s != NULL; s = s->next) { + if ((s->flags & SECTION_TYPE) == S_ZEROFILL) + continue; + + /* no padding needs to be done to the sections */ + + /* Like a.out Mach-O references things in the data or bss + * sections by addresses which are actually relative to the + * start of the _text_ section, in the _file_. See outaout.c + * for more information. */ + saa_rewind(s->data); + for (r = s->relocs; r != NULL; r = r->next) { + saa_fread(s->data, r->addr, blk, (long)r->length << 1); + p = q = blk; + l = *p++; + + /* get offset based on relocation type */ + if (r->length > 0) { + l += ((long)*p++) << 8; + + if (r->length == 2) { + l += ((long)*p++) << 16; + l += ((long)*p++) << 24; + } + } + + /* add sizes of previous sections to current offset */ + for (s2 = sects, fi = 1; + s2 != NULL && fi < r->snum; s2 = s2->next, fi++) + if ((s2->flags & SECTION_TYPE) != S_ZEROFILL) + l += s2->size; + + /* write new offset back */ + if (r->length == 2) + WRITELONG(q, l); + else if (r->length == 1) + WRITESHORT(q, l); + else + *q++ = l & 0xFF; + + saa_fwrite(s->data, r->addr, blk, (long)r->length << 1); + } + + /* dump the section data to file */ + saa_fpwrite(s->data, machofp); + } + + /* pad last section up to reloc entries on long boundary */ + fwrite(rel_paddata, rel_padcnt, 1, machofp); + + /* emit relocation entries */ + for (s = sects; s != NULL; s = s->next) + macho_write_relocs (s->relocs); +} + +/* Write out the symbol table. We should already have sorted this + before now. */ +static void macho_write_symtab (void) +{ + struct symbol *sym; + struct section *s; + long fi, i; + + /* we don't need to pad here since MACHO_RELINFO_SIZE == 8 */ + + saa_rewind(syms); + for (i = 0; i < nsyms; ++i) { + sym = saa_rstruct(syms); + + fwritelong(sym->strx, machofp); /* string table entry number */ + fwrite(&sym->type, 1, 1, machofp); /* symbol type */ + fwrite(&sym->sect, 1, 1, machofp); /* section */ + fwriteshort(sym->desc, machofp); /* description */ + + /* fix up the symbol value now we know the final section + ** sizes. */ + if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) { + for (s = sects, fi = 1; + s != NULL && fi < sym->sect; s = s->next, ++fi) + sym->value += s->size; + } + + fwritelong(sym->value, machofp); /* value (i.e. offset) */ + } +} + +/* Write out the object file. */ + +static void macho_write (void) +{ + unsigned long offset = 0; + + /* mach-o object file structure: + ** + ** mach header + ** ulong magic + ** int cpu type + ** int cpu subtype + ** ulong mach file type + ** ulong number of load commands + ** ulong size of all load commands + ** (includes section struct size of segment command) + ** ulong flags + ** + ** segment command + ** ulong command type == LC_SEGMENT + ** ulong size of load command + ** (including section load commands) + ** char[16] segment name + ** ulong in-memory offset + ** ulong in-memory size + ** ulong in-file offset to data area + ** ulong in-file size + ** (in-memory size excluding zerofill sections) + ** int maximum vm protection + ** int initial vm protection + ** ulong number of sections + ** ulong flags + ** + ** section commands + ** char[16] section name + ** char[16] segment name + ** ulong in-memory offset + ** ulong in-memory size + ** ulong in-file offset + ** ulong alignment + ** (irrelevant in MH_OBJECT) + ** ulong in-file offset of relocation entires + ** ulong number of relocations + ** ulong flags + ** ulong reserved + ** ulong reserved + ** + ** symbol table command + ** ulong command type == LC_SYMTAB + ** ulong size of load command + ** ulong symbol table offset + ** ulong number of symbol table entries + ** ulong string table offset + ** ulong string table size + ** + ** raw section data + ** + ** padding to long boundary + ** + ** relocation data (struct reloc) + ** long offset + ** uint data (symbolnum, pcrel, length, extern, type) + ** + ** symbol table data (struct nlist) + ** long string table entry number + ** uchar type + ** (extern, absolute, defined in section) + ** uchar section + ** (0 for global symbols, section number of definition (>= 1, <= + ** 254) for local symbols, size of variable for common symbols + ** [type == extern]) + ** short description + ** (for stab debugging format) + ** ulong value (i.e. file offset) of symbol or stab offset + ** + ** string table data + ** list of null-terminated strings + */ + + /* Emit the Mach-O header. */ + macho_write_header(); + + offset = MACHO_HEADER_SIZE + head_sizeofcmds; + + /* emit the segment load command */ + if (seg_nsects > 0) + offset = macho_write_segment (offset); + else error(ERR_WARNING, "no sections?"); if (nsyms > 0) { @@ -772,111 +901,32 @@ static void macho_cleanup(int debuginfo) } /* emit section data */ - if (seg_nsects > 0) { - struct section *s2; - char *rel_paddata = "\0\0\0"; - unsigned char fi, *p, *q, blk[4]; - long l; - - for (s = sects; s != NULL; s = s->next) { - if ((s->flags & SECTION_TYPE) == S_ZEROFILL) - continue; - - /* no padding needs to be done to the sections */ - - /* Like a.out Mach-O references things in the data or bss - * sections by addresses which are actually relative to the - * start of the _text_ section, in the _file_. See outaout.c - * for more information. */ - saa_rewind(s->data); - for (r = s->relocs; r != NULL; r = r->next) { - saa_fread(s->data, r->addr, blk, (long)r->length << 1); - p = q = blk; - l = *p++; - - /* get offset based on relocation type */ - if (r->length > 0) { - l += ((long)*p++) << 8; - - if (r->length == 2) { - l += ((long)*p++) << 16; - l += ((long)*p++) << 24; - } - } - - /* add sizes of previous sections to current offset */ - for (s2 = sects, fi = 1; - s2 != NULL && fi < r->snum; s2 = s2->next, fi++) - if ((s2->flags & SECTION_TYPE) != S_ZEROFILL) - l += s2->size; - - /* write new offset back */ - if (r->length == 2) - WRITELONG(q, l); - else if (r->length == 1) - WRITESHORT(q, l); - else - *q++ = l & 0xFF; - - saa_fwrite(s->data, r->addr, blk, (long)r->length << 1); - } - - /* dump the section data to file */ - saa_fpwrite(s->data, machofp); - } + if (seg_nsects > 0) + macho_write_section (); - /* pad last section up to reloc entries on long boundary */ - fwrite(rel_paddata, rel_padcnt, 1, machofp); - - /* emit relocation entries */ - for (s = sects; s != NULL; s = s->next) { - for (r = s->relocs; r != NULL; r = r->next) { - unsigned long word2; - - fwritelong(r->addr, machofp); /* reloc offset */ - - word2 = r->snum; - word2 |= r->pcrel << 24; - word2 |= r->length << 25; - word2 |= r->ext << 27; - word2 |= r->type << 28; - fwritelong(word2, machofp); /* reloc data */ - } - } - } - - /* emit symbol table */ - if (nsyms > 0) { - struct symbol *sym; - long fi, i; - - /* we don't need to pad here since MACHO_RELINFO_SIZE == 8 */ - - saa_rewind(syms); - for (i = 0; i < nsyms; ++i) { - sym = saa_rstruct(syms); - - fwritelong(sym->strx, machofp); /* string table entry number */ - fwrite(&sym->type, 1, 1, machofp); /* symbol type */ - fwrite(&sym->sect, 1, 1, machofp); /* section */ - fwriteshort(sym->desc, machofp); /* description */ - - /* fix up the symbol value now we know the final section - ** sizes. */ - if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) { - for (s = sects, fi = 1; - s != NULL && fi < sym->sect; s = s->next, ++fi) - sym->value += s->size; - } - - fwritelong(sym->value, machofp); /* value (i.e. offset) */ - } - } + /* emit symbol table if we have symbols */ + if (nsyms > 0) + macho_write_symtab (); /* we don't need to pad here since MACHO_NLIST_SIZE == 12 */ /* emit string table */ saa_fpwrite(strs, machofp); +} +/* We do quite a bit here, starting with finalizing all of the data + for the object file, writing, and then freeing all of the data from + the file. */ + +static void macho_cleanup(int debuginfo) +{ + struct section *s; + struct reloc *r; + + (void)debuginfo; + + /* First calculate and finalize needed values. */ + macho_calculate_sizes(); + macho_write(); /* done - yay! */ fclose(machofp); |