#include <ctype.h>
#include <time.h>
#include <stdint.h>
#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "mangle.h"
#include "fixup.h"
#include "segment.h"
#include "extern.h"
#include "macho64.h"
#include "macho64spec.h"
#include "myassert.h"
#define MACHOABI_OSX64 1
#define ROUND_UP( i, r ) (((i)+((r)-1)) & ~((r)-1))
struct strentry
{
char *pstr;
struct strentry *next;
struct asym *sym;
int idx;
int offset;
};
struct macho_section_entry
{
char *srcName;
struct section_64 section;
struct section_64 *next;
int dif;
int size;
int ofs;
void *data;
int idx;
int relocCount;
uint64_t relocOfs;
};
struct macho_module
{
char *srcname;
struct mach_header_64 header;
struct segment_command_64 *pSegCmd;
struct macho_section_entry *sections;
struct symtab_command *pSymTblCmd;
struct dysymtab_command *pdySymTblCmd;
struct strentry *strings;
int relocCount;
int sectAlign;
int symIdx;
int extSymIdx;
int undefSymIdx;
int symCount;
int extSymCount;
int undefSymCount;
};
static int padbyte[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int macho_section_count()
{
int cnt = 0;
struct dsym *curr;
for (curr = SymTables[TAB_SEG].head; curr; curr = curr->next)
{
if (strcmp(curr->sym.name, "_BSS") == 0)
cnt++;
else if (strcmp(curr->sym.name, "CONST") == 0)
cnt++;
else if (strcmp(curr->sym.name, "_TEXT") == 0)
cnt++;
else if (strcmp(curr->sym.name, "_DATA") == 0)
cnt++;
}
return(cnt);
}
struct segment_command_64 * macho_build_seg_cmd( int fileofs )
{
struct segment_command_64 *pCmd = NULL;
pCmd = malloc(sizeof(struct segment_command_64));
memset(pCmd,0,sizeof(struct segment_command_64));
pCmd->cmd = LC_SEGMENT_64;
pCmd->cmdsize = sizeof(struct segment_command_64) + (sizeof(struct section_64) * macho_section_count());
pCmd->flags = 0;
pCmd->initprot = VM_PROT_ALL;
pCmd->maxprot = VM_PROT_ALL;
pCmd->nsects = macho_section_count();
pCmd->vmaddr = 0;
pCmd->fileoff = fileofs;
pCmd->filesize = 0;
pCmd->vmsize = 0;
return(pCmd);
}
struct symtab_command * macho_build_symtbl_cmd()
{
struct symtab_command *pCmd = NULL;
pCmd = malloc(sizeof(struct symtab_command));
memset(pCmd, 0, sizeof(struct symtab_command));
pCmd->cmd = LC_SYMTAB;
pCmd->cmdsize = sizeof(struct symtab_command);
pCmd->nsyms = SymGetCount();
pCmd->symoff = 0;
pCmd->stroff = 0;
pCmd->strsize = 0;
return(pCmd);
}
struct dysymtab_command * macho_build_dysymtbl_cmd()
{
struct dysymtab_command *pCmd = NULL;
pCmd = malloc(sizeof(struct dysymtab_command));
memset(pCmd, 0, sizeof(struct dysymtab_command));
pCmd->cmd = LC_DYSYMTAB;
pCmd->cmdsize = sizeof(struct dysymtab_command);
return(pCmd);
}
static void macho_add_string(struct strentry *pstr, struct macho_module *mm)
{
struct strentry *pCurrStr = mm->strings;
int lastIdx = 1;
int ofs = 1;
if (pCurrStr == NULL)
{
pstr->idx = 1;
pstr->offset = 1;
mm->strings = pstr;
}
else
{
ofs += strlen(pCurrStr->pstr) + 1;
while (pCurrStr->next != NULL)
{
pCurrStr = pCurrStr->next;
ofs += strlen(pCurrStr->pstr) + 1;
lastIdx = pCurrStr->idx;
}
pstr->idx = lastIdx + 1;
pstr->offset = ofs;
pCurrStr->next = pstr;
}
return;
}
int macho_build_string_tbl(struct symtab_command *pSymCmd, struct macho_module *mm)
{
int tblSize = 0;
int i = 0;
struct asym *sym = NULL;
struct strentry *pstr = NULL;
int totalSymCount = 0;
while (sym = SymEnum(sym, &i))
{
if (strcmp(sym->name, "$xdatasym") == 0) continue;
if (sym->state != SYM_MACRO && sym->state != SYM_SEG && sym->state != SYM_TMACRO && sym->predefined == 0 && sym->state != SYM_GRP && sym->isequate == 0)
{
if (sym->state != SYM_EXTERNAL && !sym->ispublic && sym->used)
{
tblSize += strlen(sym->name) + 1;
pstr = malloc(sizeof(struct strentry));
memset(pstr, 0, sizeof(struct strentry));
pstr->pstr = sym->name;
pstr->sym = sym;
macho_add_string(pstr, mm);
mm->symCount++;
totalSymCount++;
}
}
}
mm->extSymIdx = totalSymCount;
while (sym = SymEnum(sym, &i))
{
if (sym->state != SYM_MACRO && sym->state != SYM_SEG && sym->state != SYM_TMACRO && sym->predefined == 0 && sym->state != SYM_GRP && sym->isequate == 0)
{
if (sym->ispublic)
{
tblSize += strlen(sym->name) + 1;
pstr = malloc(sizeof(struct strentry));
memset(pstr, 0, sizeof(struct strentry));
pstr->pstr = sym->name;
pstr->sym = sym;
macho_add_string(pstr, mm);
mm->extSymCount++;
totalSymCount++;
}
}
}
mm->undefSymIdx = totalSymCount;
while (sym = SymEnum(sym, &i))
{
if (sym->state != SYM_MACRO && sym->state != SYM_SEG && sym->state != SYM_TMACRO && sym->predefined == 0 && sym->state != SYM_GRP && sym->isequate == 0)
{
if (sym->state == SYM_EXTERNAL)
{
tblSize += strlen(sym->name) + 1;
pstr = malloc(sizeof(struct strentry));
memset(pstr, 0, sizeof(struct strentry));
pstr->pstr = sym->name;
pstr->sym = sym;
macho_add_string(pstr, mm);
mm->undefSymCount++;
totalSymCount++;
}
}
}
return(tblSize);
}
static int GetSymbolIndex(const char *pName, struct macho_module *mm)
{
struct strentry *currStr = mm->strings;
int idx = 0;
for (currStr = mm->strings;currStr;currStr = currStr->next)
{
if (strcmp(pName, currStr->pstr) == 0)
{
idx = currStr->idx;
break;
}
}
return(idx);
}
struct section_64 * macho_build_section( const char *secName, const char *segName, uint32_t flags, const char *srcName )
{
struct macho_section_entry *pSec = NULL;
pSec = malloc(sizeof(struct macho_section_entry));
memset(pSec, 0, sizeof(struct macho_section_entry));
pSec->srcName = srcName;
strcpy(pSec->section.sectname, secName);
strcpy(pSec->section.segname, segName);
pSec->section.align = 0; pSec->section.flags = flags;
return(pSec);
}
static void macho_add_section(struct macho_section_entry *pSec, struct macho_module *mm)
{
struct macho_section_entry *pCurrSec = mm->sections;
if (pCurrSec == NULL)
mm->sections = pSec;
else
{
while (pCurrSec->next != NULL)
{
pCurrSec = pCurrSec->next;
}
pCurrSec->next = pSec;
}
mm->header.sizeofcmds += sizeof(struct section_64);
return;
}
static int GetSectionIdx(struct asym *seg, struct macho_module *mm)
{
struct macho_section_entry *curr = NULL;
if (seg)
{
for (curr = mm->sections; curr; curr = curr->next)
{
if (strcmp(seg->name, curr->srcName) == 0)
{
return(curr->idx);
}
}
}
return(0);
}
static int GetSegFixups(struct dsym *seg)
{
struct fixup *curr;
int fixupcnt = 0;
for (curr = seg->e.seginfo->FixupList.head;curr;curr = curr->nextrlc)
{
fixupcnt++;
}
return(fixupcnt);
}
static int GetRelocationCount(struct macho_module *mm, int baseOfs)
{
struct macho_section_entry *curr = NULL;
struct dsym *seg;
int relocCnt = 0;
int relocOfs = 0;
for (curr = mm->sections; curr; curr = curr->next)
{
for (seg = SymTables[TAB_SEG].head; seg; seg = seg->next)
{
if (strcmp(seg->sym.name, curr->srcName) == 0)
{
curr->relocOfs = baseOfs + relocOfs;
relocOfs += (GetSegFixups(seg) * sizeof(struct relocation_info));
relocCnt += GetSegFixups(seg);
curr->relocCount = GetSegFixups(seg);
curr->section.nreloc = curr->relocCount;
curr->section.reloff = curr->relocOfs;
if (curr->section.nreloc == 0)
curr->section.reloff = 0;
break;
}
}
}
return(relocCnt);
}
static void macho_build_structures( struct module_info *modinfo, struct macho_module mm )
{
int cnt = 0;
struct dsym *curr;
struct dsym *seg = NULL;
struct macho_section_entry *currSec;
struct strentry *currStr;
int fileofs = sizeof(struct mach_header_64);
uint64_t currAddr = 0;
struct segment_command_64 *pCmd = NULL;
struct symtab_command *pSymCmd = NULL;
struct dysymtab_command *pdySymCmd = NULL;
int sectionDataSize = 0;
int stringTableSize = 0;
int secIdx = 1;
struct nlist_64 symEntry;
int relocDataSize = 0;
struct relocation_info reloc;
struct fixup *currFixup;
uint_8 machotype;
struct version_min_command ver;
int ofsAdj = 0;
mm.sectAlign = 1;
pCmd = macho_build_seg_cmd( fileofs );
mm.pSegCmd = pCmd;
mm.header.ncmds++;
mm.header.sizeofcmds += sizeof(struct segment_command_64);
fileofs += sizeof(struct segment_command_64);
ver.cmd = LC_VERSION_MIN_MACOSX;
ver.cmdsize = sizeof(struct version_min_command);
ver.version = 0x000a0c00; ver.sdk = 0;
mm.header.ncmds++;
mm.header.sizeofcmds += sizeof(struct version_min_command);
fileofs += sizeof(struct version_min_command);
for (curr = SymTables[TAB_SEG].head; curr; curr = curr->next)
{
if (strcmp(curr->sym.name, "_TEXT") == 0)
{
currSec = macho_build_section("__text", "__TEXT", S_REGULAR, curr->sym.name);
macho_add_section(currSec, &mm);
currSec->data = curr->e.seginfo->CodeBuffer;
currSec->size = ROUND_UP(curr->e.seginfo->bytes_written,mm.sectAlign);
currSec->dif = currSec->size - curr->e.seginfo->bytes_written;
currSec->section.size = currSec->size;
currSec->section.addr = currAddr;
currAddr += currSec->size;
currAddr = ROUND_UP(currAddr, mm.sectAlign);
pCmd->filesize += currSec->size;
currSec->idx = secIdx++;
currSec->section.flags |= (S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
fileofs += sizeof(struct section_64);
pCmd->vmsize += currSec->size;
}
else if (strcmp(curr->sym.name, "_DATA") == 0)
{
currSec = macho_build_section("__data", "__DATA", S_REGULAR, curr->sym.name);
macho_add_section(currSec, &mm);
currSec->data = curr->e.seginfo->CodeBuffer;
currSec->size = ROUND_UP(curr->e.seginfo->bytes_written, mm.sectAlign);
currSec->dif = currSec->size - curr->e.seginfo->bytes_written;
currSec->section.size = currSec->size;
currSec->section.addr = currAddr;
currAddr += currSec->size;
currAddr = ROUND_UP(currAddr, mm.sectAlign);
pCmd->filesize += currSec->size;
currSec->idx = secIdx++;
fileofs += sizeof(struct section_64);
pCmd->vmsize += currSec->size;
}
else if (strcmp(curr->sym.name, "CONST") == 0)
{
currSec = macho_build_section("_rdata", "__DATA", S_REGULAR, curr->sym.name);
macho_add_section(currSec, &mm);
currSec->data = curr->e.seginfo->CodeBuffer;
currSec->size = ROUND_UP(curr->e.seginfo->bytes_written, mm.sectAlign);
currSec->dif = currSec->size - curr->e.seginfo->bytes_written;
currSec->section.size = currSec->size;
currSec->section.addr = currAddr;
currAddr += currSec->size;
currAddr = ROUND_UP(currAddr, mm.sectAlign);
pCmd->filesize += currSec->size;
currSec->idx = secIdx++;
fileofs += sizeof(struct section_64);
pCmd->vmsize += currSec->size;
}
else if (strcmp(curr->sym.name, "_BSS") == 0)
{
currSec = macho_build_section("__bss", "__DATA", S_ZEROFILL, curr->sym.name);
macho_add_section(currSec, &mm);
currSec->data = curr->e.seginfo->CodeBuffer;
currSec->size = ROUND_UP(curr->e.seginfo->bytes_written, mm.sectAlign);
currSec->dif = currSec->size - curr->e.seginfo->bytes_written;
currSec->section.size = currSec->size;
currSec->section.addr = currAddr;
currAddr = ROUND_UP(currAddr, mm.sectAlign);
currAddr += currSec->size;
currSec->idx = secIdx++;
fileofs += sizeof(struct section_64);
pCmd->vmsize += currSec->size;
}
}
pSymCmd = macho_build_symtbl_cmd();
mm.pSymTblCmd = pSymCmd;
mm.header.ncmds++;
mm.header.sizeofcmds += sizeof(struct symtab_command);
fileofs += sizeof(struct symtab_command);
pdySymCmd = macho_build_dysymtbl_cmd();
mm.pdySymTblCmd = pdySymCmd;
mm.header.ncmds++;
mm.header.sizeofcmds += sizeof(struct dysymtab_command);
fileofs += sizeof(struct dysymtab_command);
sectionDataSize = 0;
for (currSec = mm.sections;currSec;currSec = currSec->next)
{
currSec->section.offset = fileofs + sectionDataSize;
currSec->ofs = fileofs + sectionDataSize;
sectionDataSize += currSec->size;
}
pCmd->fileoff = mm.sections->section.offset;
stringTableSize = macho_build_string_tbl(pSymCmd,&mm) + 1;
pSymCmd->nsyms = mm.symCount;
mm.relocCount = GetRelocationCount(&mm, fileofs + sectionDataSize);
relocDataSize = mm.relocCount * sizeof(struct relocation_info);
pdySymCmd->nlocalsym = mm.symCount;
pdySymCmd->ilocalsym = 0;
pdySymCmd->nextdefsym = mm.extSymCount;
pdySymCmd->iextdefsym = mm.extSymIdx;
pdySymCmd->nundefsym = mm.undefSymCount;
pdySymCmd->iundefsym = mm.undefSymIdx;
pSymCmd->nsyms = mm.symCount + mm.extSymCount + mm.undefSymCount;
pSymCmd->symoff = fileofs + sectionDataSize + relocDataSize;
pSymCmd->strsize = stringTableSize;
pSymCmd->stroff = fileofs + sectionDataSize + (sizeof(struct nlist_64)*pSymCmd->nsyms) + relocDataSize;
if (fwrite(&mm.header, 1, sizeof(mm.header), CurrFile[OBJ]) != sizeof(mm.header))
WriteError();
if (fwrite(pCmd, 1, sizeof(struct segment_command_64), CurrFile[OBJ]) != sizeof(struct segment_command_64))
WriteError();
for (currSec = mm.sections;currSec;currSec = currSec->next)
{
if (fwrite(&currSec->section, 1, sizeof(struct section_64), CurrFile[OBJ]) != sizeof(struct section_64))
WriteError();
}
if (fwrite(&ver, 1, sizeof(struct version_min_command), CurrFile[OBJ]) != sizeof(struct version_min_command))
WriteError();
if (fwrite(pSymCmd, 1, sizeof(struct symtab_command), CurrFile[OBJ]) != sizeof(struct symtab_command))
WriteError();
if (fwrite(pdySymCmd, 1, sizeof(struct dysymtab_command), CurrFile[OBJ]) != sizeof(struct dysymtab_command))
WriteError();
for (currSec = mm.sections;currSec;currSec = currSec->next)
{
if (fwrite(currSec->data, 1, currSec->size-currSec->dif, CurrFile[OBJ]) != currSec->size-currSec->dif)
WriteError();
if (currSec->dif > 0)
{
fwrite(&padbyte, 1, currSec->dif, CurrFile[OBJ]);
}
}
for (currSec = mm.sections; currSec; currSec = currSec->next)
{
for (seg = SymTables[TAB_SEG].head; seg; seg = seg->next)
{
if (strcmp(seg->sym.name, currSec->srcName) == 0)
{
for (currFixup = seg->e.seginfo->FixupList.head; currFixup; currFixup = currFixup->nextrlc)
{
memset(&reloc, 0, sizeof(struct relocation_info));
reloc.r_address = currFixup->locofs;
if (currFixup->sym)
{
reloc.r_extern = 1;
reloc.r_symbolnum = GetSymbolIndex(currFixup->sym->name, &mm) - 1;
reloc.r_pcrel = 1;
}
else
{
reloc.r_extern = 0;
reloc.r_symbolnum = currSec->idx;
reloc.r_pcrel = 1;
}
if (currFixup->type == FIX_RELOFF32 && currFixup->addbytes == 4 && currFixup->option == OPTJ_NONE)
{
machotype = X86_64_RELOC_SIGNED;
}
else if (currFixup->type == FIX_RELOFF32 && (currFixup->option == OPTJ_EXPLICIT || currFixup->option == OPTJ_CALL || currFixup->option == OPTJ_JXX))
{
machotype = X86_64_RELOC_BRANCH;
}
else
{
machotype = X86_64_RELOC_SIGNED;
}
reloc.r_type = machotype;
switch (currFixup->addbytes)
{
case 8:
reloc.r_length = 3;
break;
case 4:
reloc.r_length = 2;
break;
case 2:
reloc.r_length = 1;
break;
default:
reloc.r_length = 0;
}
if (fwrite(&reloc, 1, sizeof(struct relocation_info), CurrFile[OBJ]) != sizeof(struct relocation_info))
WriteError();
}
break;
}
}
}
for (currStr = mm.strings;currStr;currStr = currStr->next)
{
memset(&symEntry, 0, sizeof(struct nlist_64));
if (currStr->sym->state == SYM_EXTERNAL)
{
symEntry.n_sect = NO_SECT;
symEntry.n_type = N_UNDF | N_EXT;
symEntry.n_value = currStr->sym->total_size;
symEntry.n_un.n_strx = currStr->offset;
}
else
{
symEntry.n_sect = GetSectionIdx(currStr->sym->segment, &mm);
if (currStr->sym->ispublic)
symEntry.n_type = N_SECT | N_EXT;
else
symEntry.n_type = N_SECT;
symEntry.n_value = (uint64_t)currStr->sym->offset;
ofsAdj = 0;
for (currSec = mm.sections;currSec->idx < symEntry.n_sect;currSec = currSec->next)
ofsAdj += currSec->size;
symEntry.n_value += ofsAdj;
symEntry.n_un.n_strx = currStr->offset;
}
symEntry.n_desc = 0;
if (currStr->sym->weak)
symEntry.n_desc |= N_WEAK_DEF;
if (fwrite(&symEntry, 1, sizeof(struct nlist_64), CurrFile[OBJ]) != sizeof(struct nlist_64))
WriteError();
fileofs += sizeof(struct nlist_64);
}
if (fwrite(&padbyte, 1, 1, CurrFile[OBJ]) != 1)
WriteError();
for (currStr = mm.strings;currStr;currStr = currStr->next)
{
int len = strlen(currStr->pstr) + 1;
if (fwrite(currStr->pstr, 1, len, CurrFile[OBJ]) != len)
WriteError();
}
}
static ret_code macho_write_module( struct module_info *modinfo )
{
struct macho_module mm;
int fileofs = 0;
DebugMsg(("macho_write_module: enter\n"));
memset(&mm, 0, sizeof(mm));
mm.srcname = CurrFName[ASM];
#if 1
mm.srcname += strlen(mm.srcname);
while (mm.srcname > CurrFName[ASM] &&
*(mm.srcname - 1) != '/' &&
*(mm.srcname - 1) != '\\') mm.srcname--;
#endif
fseek(CurrFile[OBJ], 0, SEEK_SET);
if( modinfo->defOfssize == USE64)
{
mm.header.magic = MH_MAGIC_64;
mm.header.cputype = CPU_TYPE_X86_64;
mm.header.cpusubtype = CPU_SUBTYPE_LITTLE_ENDIAN | CPU_SUBTYPE_X86_64_ALL;
mm.header.filetype = MH_OBJECT;
mm.header.flags = NULL;
macho_build_structures(modinfo, mm);
}
else
{
EmitError(MACHO_NOT_64);
return(ERROR);
}
DebugMsg(("macho_write_module: exit\n"));
return(NOT_ERROR);
}
void macho_init( struct module_info *modinfo )
{
modinfo->osx_osabi = MACHOABI_OSX64;
modinfo->g.WriteModule = macho_write_module;
return;
}