#include "compiler/MachO.h" #include "compiler/CError.h" #include "compiler/CParser.h" #include "compiler/CPrep.h" #include "compiler/CompilerTools.h" #include "compiler/ObjGenMachO.h" #include "cos.h" static MachOSegment *FirstSeg; static MachOSegment *LastSeg; static UInt32 SectNum; static UInt32 SymNum; static UInt32 NumStabs; static UInt32 ilocalsym; static UInt32 nlocalsym; static UInt32 iextdefsym; static UInt32 nextdefsym; static UInt32 iundefsym; static UInt32 nundefsym; static MachOSymbol *FirstSym; static MachOSymbol *LastSym; static MachOSymbol *FirstStab; static MachOSymbol *LastStab; static UInt32 FileOffset; static UInt32 VmAddr; static GList ObjFile; static SInt32 SymPad; static UInt32 CodeSize; static UInt32 IdataSize; static UInt32 UdataSize; static GList IndirectSymbolTable; static GList StringTable; static UInt32 IndirectSymbolTableOffset; void MachO_Setup(void) { FirstSeg = LastSeg = NULL; FirstSym = LastSym = NULL; FirstStab = LastStab = NULL; SectNum = 0; SymNum = 0; NumStabs = 0; ilocalsym = -1; nlocalsym = 0; iextdefsym = -1; nextdefsym = 0; iundefsym = -1; nundefsym = 0; InitGList(&IndirectSymbolTable, 256); InitGList(&StringTable, 4096); AppendGListByte(&StringTable, 0); } static UInt32 GetSectVMAddr(UInt32 id) { MachOSegment *segment; MachOSection *section; for (segment = FirstSeg; segment; segment = segment->next) { for (section = segment->firstSection; section; section = section->next) { if (section->num == id) return section->section.addr; } } return 0; } static UInt32 AllocateForLoadCommands(void) { UInt32 ncmds = 0; MachOSegment *segment; MachOSection *section; for (segment = FirstSeg; segment; segment = segment->next) { FileOffset += sizeof(struct segment_command); segment->cmd.cmdsize += sizeof(struct segment_command); ncmds++; for (section = segment->firstSection; section; section = section->next) { segment->cmd.cmdsize += sizeof(struct section); FileOffset += sizeof(struct section); } } return ncmds; } static UInt32 AlignModulus[] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800 }; static void AllocateAddresses(void) { MachOSegment *segment; MachOSection *section; UInt32 pad; for (segment = FirstSeg; segment; segment = segment->next) { segment->cmd.vmaddr = VmAddr; segment->cmd.fileoff = FileOffset; for (section = segment->firstSection; section; section = section->next) { if (section->glist.size) section->section.size = section->glist.size; pad = AlignModulus[section->section.align] - (VmAddr % AlignModulus[section->section.align]); pad %= AlignModulus[section->section.align]; VmAddr += pad; section->section.addr = VmAddr; VmAddr += section->section.size; FileOffset += pad; if (section->glist.size) { section->section.offset = FileOffset; FileOffset += section->glist.size; } else { section->section.offset = FileOffset; } if (!strncmp(section->section.segname, "__TEXT", 6)) { CodeSize += section->section.size; } else { if (section->glist.size) IdataSize += section->section.size; else UdataSize += section->section.size; } } segment->cmd.filesize = FileOffset - segment->cmd.fileoff; segment->cmd.vmsize = VmAddr - segment->cmd.vmaddr; } } static void ApplyRelocs(void) { MachOSegment *segment; MachOSection *section; MachOReloc *reloc; enum reloc_type_ppc pairType; UInt32 pairValue; UInt32 opMask; UInt32 argMask; UInt32 value; UInt32 *ptr; for (segment = FirstSeg; segment; segment = segment->next) { for (section = segment->firstSection; section; section = section->next) { for (reloc = section->firstReloc; reloc; reloc = reloc->next) { if (reloc->is_extern) { opMask = 0xFFFFFFFF; argMask = 0; value = 0; switch (reloc->reltype) { case PPC_RELOC_HI16: case PPC_RELOC_LO16: case PPC_RELOC_HA16: pairValue = 0; pairType = reloc->reltype; break; } } else if (reloc->reltype == PPC_RELOC_PAIR) { if (reloc->value != 0xFFFFFF) { value = pairValue - (reloc->value + section->section.addr); value += reloc->address; } else { value = pairValue + reloc->address; } switch (pairType) { case PPC_RELOC_HI16: opMask = 0xFFFF0000; argMask = 0xFFFF; value >>= 16; break; case PPC_RELOC_HA16: opMask = 0xFFFF0000; argMask = 0xFFFF; if (value & 0x8000) value += 0x10000; value >>= 16; break; case PPC_RELOC_LO16: opMask = 0xFFFF0000; argMask = 0xFFFF; value = value & 0xFFFF; break; case PPC_RELOC_HI16_SECTDIFF: opMask = 0xFFFF0000; argMask = 0xFFFF; value >>= 16; break; case PPC_RELOC_HA16_SECTDIFF: opMask = 0xFFFF0000; argMask = 0xFFFF; if (value & 0x8000) value += 0x10000; value >>= 16; break; case PPC_RELOC_LO16_SECTDIFF: opMask = 0xFFFF0000; argMask = 0xFFFF; value = value & 0xFFFF; break; case PPC_RELOC_SECTDIFF: opMask = 0; argMask = 0xFFFFFFFF; break; default: CError_FATAL(388); } } else { value = GetSectVMAddr(reloc->value); switch (reloc->reltype) { case PPC_RELOC_VANILLA: opMask = 0; argMask = 0xFFFFFFFF; break; case PPC_RELOC_BR14: opMask = 0xFFFF0003; argMask = 0xFFFC; break; case PPC_RELOC_BR24: opMask = 0xFC000003; argMask = 0x3FFFFFC; break; case PPC_RELOC_LO14: opMask = 0xFFFF0003; argMask = 0xFFFC; break; case PPC_RELOC_HI16: case PPC_RELOC_HA16: case PPC_RELOC_LO16: case PPC_RELOC_HI16_SECTDIFF: case PPC_RELOC_HA16_SECTDIFF: case PPC_RELOC_LO16_SECTDIFF: case PPC_RELOC_SECTDIFF: // first half of a pair opMask = 0xFFFF0000; argMask = 0xFFFF; pairValue = value; pairType = reloc->reltype; value = 0; break; case PPC_RELOC_PB_LA_PTR: CError_FATAL(428); break; default: CError_FATAL(432); } } if (reloc->reltype != PPC_RELOC_PAIR) ptr = (UInt32 *) ((*section->glist.data) + reloc->address); if (reloc->is_pcrel) { if (!reloc->is_extern) *ptr = (*ptr & opMask) | (argMask & (value - (reloc->address + section->section.addr) + (*ptr & argMask))); } else { if (reloc->reltype == PPC_RELOC_PAIR) *ptr = (*ptr & opMask) | (value & argMask); else *ptr = (*ptr & opMask) | (argMask & (value + (*ptr & argMask))); } } } } } static void AllocForRelocs(void) { MachOSegment *segment; MachOSection *section; for (segment = FirstSeg; segment; segment = segment->next) { for (section = segment->firstSection; section; section = section->next) { if (section->section.nreloc) { section->section.reloff = FileOffset; FileOffset += section->section.nreloc * 8; } } } } static void WriteSegLoadCommands(void) { MachOSegment *segment; MachOSection *section; for (segment = FirstSeg; segment; segment = segment->next) { AppendGListData(&ObjFile, &segment->cmd, sizeof(segment->cmd)); for (section = segment->firstSection; section; section = section->next) AppendGListData(&ObjFile, §ion->section, sizeof(section->section)); } } static void WriteSymtabLoadCommand(void) { struct symtab_command cmd; UInt32 total; cmd.cmd = LC_SYMTAB; cmd.cmdsize = sizeof(cmd); cmd.symoff = FileOffset; total = SymNum + NumStabs; cmd.nsyms = total; FileOffset += total * sizeof(struct nlist); cmd.stroff = FileOffset; cmd.strsize = StringTable.size; AppendGListData(&ObjFile, &cmd, sizeof(cmd)); } static void WriteDynamicSymtabLoadCommand(void) { struct dysymtab_command cmd; if (!nlocalsym) ilocalsym = 0; if (!nextdefsym) iextdefsym = ilocalsym + nlocalsym; if (!nundefsym) iundefsym = iextdefsym + nextdefsym; ilocalsym += NumStabs; iextdefsym += NumStabs; iundefsym += NumStabs; CError_ASSERT(644, (ilocalsym + nlocalsym) <= (SymNum + NumStabs)); CError_ASSERT(648, (iextdefsym + nextdefsym) <= (SymNum + NumStabs)); CError_ASSERT(652, (iundefsym + nundefsym) <= (SymNum + NumStabs)); cmd.cmd = LC_DYSYMTAB; cmd.cmdsize = sizeof(cmd); cmd.ilocalsym = ilocalsym; cmd.nlocalsym = nlocalsym; cmd.iextdefsym = iextdefsym; cmd.nextdefsym = nextdefsym; cmd.iundefsym = iundefsym; cmd.nundefsym = nundefsym; cmd.tocoff = 0; cmd.ntoc = 0; cmd.modtaboff = 0; cmd.nmodtab = 0; cmd.extrefsymoff = 0; cmd.nextrefsyms = 0; cmd.indirectsymoff = IndirectSymbolTableOffset; cmd.nindirectsyms = IndirectSymbolTable.size / 4; cmd.extreloff = 0; cmd.nextrel = 0; cmd.locreloff = 0; cmd.nlocrel = 0; AppendGListData(&ObjFile, &cmd, sizeof(cmd)); } static void WriteSectionData(void) { MachOSegment *segment; MachOSection *section; UInt32 pad; VmAddr = 0; for (segment = FirstSeg; segment; segment = segment->next) { for (section = segment->firstSection; section; section = section->next) { pad = AlignModulus[section->section.align] - (VmAddr % AlignModulus[section->section.align]); pad %= AlignModulus[section->section.align]; while (pad) { AppendGListByte(&ObjFile, 0); VmAddr++; FileOffset++; pad--; } if (section->glist.size) { CError_ASSERT(711, ObjFile.size == section->section.offset); COS_LockHandle(section->glist.data); AppendGListData(&ObjFile, *section->glist.data, section->glist.size); COS_UnlockHandle(section->glist.data); VmAddr += section->glist.size; FreeGList(§ion->glist); } else { VmAddr += pad + section->section.size; } } } } static void WriteRelocs(void) { MachOSegment *segment; MachOSection *section; MachOReloc *reloc; enum reloc_type_ppc pairType; UInt32 pairValue; SInt32 scatterFlag; UInt32 combo; static char length_code[] = { 0, 0, 1, 1, 2 }; pairType = 0; pairValue = 0; scatterFlag = 0; for (segment = FirstSeg; segment; segment = segment->next) { for (section = segment->firstSection; section; section = section->next) { for (reloc = section->firstReloc; reloc; reloc = reloc->next) { if (reloc->is_extern) reloc->value += NumStabs; switch (reloc->reltype) { case PPC_RELOC_LO16: case PPC_RELOC_HA16: case PPC_RELOC_LO14: pairType = reloc->reltype; if (reloc->is_extern) { pairValue = 0; } else { pairValue = reloc->next->address + GetSectVMAddr(reloc->value); } case PPC_RELOC_VANILLA: case PPC_RELOC_BR14: case PPC_RELOC_BR24: case PPC_RELOC_HI16: case PPC_RELOC_PB_LA_PTR: AppendGListLong(&ObjFile, reloc->address); AppendGListLong(&ObjFile, (reloc->value << 8) | (reloc->is_pcrel << 7) | (length_code[reloc->length] << 5) | (reloc->is_extern << 4) | reloc->reltype ); break; case PPC_RELOC_PAIR: switch (pairType) { case PPC_RELOC_HI16: case PPC_RELOC_HA16: scatterFlag = 0; reloc->address = pairValue & 0xFFFF; break; case PPC_RELOC_LO16: scatterFlag = 0; reloc->address = pairValue >> 16; break; case PPC_RELOC_HI16_SECTDIFF: case PPC_RELOC_HA16_SECTDIFF: scatterFlag = R_SCATTERED; reloc->value += section->section.addr; pairValue -= reloc->value; reloc->address = pairValue & 0xFFFF; break; case PPC_RELOC_LO16_SECTDIFF: scatterFlag = R_SCATTERED; reloc->value += section->section.addr; pairValue -= reloc->value; reloc->address = pairValue >> 16; break; default: CError_FATAL(891); reloc->address = 0; break; } pairValue = 0; pairType = 0; if (scatterFlag) { AppendGListLong(&ObjFile, scatterFlag | (reloc->is_pcrel << 30) | (length_code[reloc->length] << 28) | (reloc->reltype << 24) | reloc->address ); AppendGListLong(&ObjFile, reloc->value); } else { combo = (reloc->value << 8) | (reloc->is_pcrel << 7) | (length_code[reloc->length] << 5) | reloc->reltype; AppendGListLong(&ObjFile, reloc->address); AppendGListLong(&ObjFile, combo); } break; case PPC_RELOC_SECTDIFF: case PPC_RELOC_HI16_SECTDIFF: case PPC_RELOC_LO16_SECTDIFF: case PPC_RELOC_HA16_SECTDIFF: // build scattered relocation reloc->value = reloc->next->address + GetSectVMAddr(reloc->value); pairType = reloc->reltype; pairValue = reloc->value; AppendGListLong(&ObjFile, R_SCATTERED | (reloc->is_pcrel << 30) | (length_code[reloc->length] << 28) | (reloc->reltype << 24) | reloc->address ); AppendGListLong(&ObjFile, reloc->value); break; default: CError_FATAL(930); } } } } } static void WriteIndirectSymbolTable(void) { UInt32 i; UInt32 *ptr; while (SymPad) { AppendGListByte(&ObjFile, 0); SymPad--; } COS_LockHandle(IndirectSymbolTable.data); ptr = (UInt32 *) *IndirectSymbolTable.data; for (i = 0; i < MachO_NumIndirectSym(); ptr++, i++) *ptr += NumStabs; AppendGListData(&ObjFile, *IndirectSymbolTable.data, IndirectSymbolTable.size); COS_UnlockHandle(IndirectSymbolTable.data); FreeGList(&IndirectSymbolTable); } static void WriteSymbolTable(void) { MachOSymbol *symbol; struct nlist nlist; if (FirstStab) { LastStab->next = FirstSym; FirstSym = FirstStab; } for (symbol = FirstSym; symbol; symbol = symbol->next) { nlist.n_strx = symbol->data.u.strx; nlist.n_type = symbol->data.type; if (symbol->data.section) nlist.n_sect = symbol->data.section->num; else nlist.n_sect = 0; nlist.n_desc = symbol->data.desc; if ( symbol->data.type == N_STSYM || symbol->data.type == N_FUN || symbol->data.type == N_LCSYM || symbol->data.type == N_SLINE || symbol->data.type == N_SO || symbol->data.type == N_SOL || symbol->data.type == N_ENTRY || symbol->data.type == N_ECOML || (symbol->data.type & N_TYPE) == N_SECT ) { if (symbol->data.section) symbol->data.value += symbol->data.section->section.addr; else CError_FATAL(1010); } nlist.n_value = symbol->data.value; AppendGListData(&ObjFile, &nlist, sizeof(nlist)); } } static void WriteStringTable(void) { COS_LockHandle(StringTable.data); AppendGListData(&ObjFile, *StringTable.data, StringTable.size); COS_UnlockHandle(StringTable.data); } void MachO_Finish(void) { struct mach_header hdr; CodeSize = 0; IdataSize = UdataSize = 0; VmAddr = 0; FileOffset = sizeof(hdr); hdr.ncmds = AllocateForLoadCommands(); FileOffset += 24; // what am I? hdr.ncmds++; if (copts.codegen_dynamic) { FileOffset += 80; // what am I? hdr.ncmds++; } hdr.sizeofcmds = FileOffset - sizeof(hdr); AllocateAddresses(); ApplyRelocs(); AllocForRelocs(); SymPad = (4 - (FileOffset & 3)) & 3; FileOffset += SymPad; IndirectSymbolTableOffset = 0; if (IndirectSymbolTable.size > 0) { IndirectSymbolTableOffset = FileOffset; FileOffset += IndirectSymbolTable.size; } InitGList(&ObjFile, 4096); hdr.magic = MH_MAGIC; hdr.cputype = CPU_TYPE_POWERPC; hdr.cpusubtype = CPU_SUBTYPE_MC98000_ALL; hdr.filetype = MH_OBJECT; hdr.flags = 0; AppendGListData(&ObjFile, &hdr, sizeof(hdr)); WriteSegLoadCommands(); WriteSymtabLoadCommand(); if (copts.codegen_dynamic) WriteDynamicSymtabLoadCommand(); WriteSectionData(); WriteRelocs(); WriteIndirectSymbolTable(); WriteSymbolTable(); WriteStringTable(); COS_ResizeHandle(ObjFile.data, ObjFile.size); cparamblkptr->objectDataHandle = ObjFile.data; cparamblkptr->objectdata.codesize = CodeSize; cparamblkptr->objectdata.udatasize = UdataSize; cparamblkptr->objectdata.idatasize = IdataSize; } void MachO_Cleanup(void) { if (StringTable.data) FreeGList(&StringTable); if (IndirectSymbolTable.data) FreeGList(&IndirectSymbolTable); } MachOSegment *MachO_CreateSegment(char *segname, int maxprot, int initprot, UInt32 flags) { MachOSegment *segment = galloc(sizeof(MachOSegment)); memset(&segment->cmd, 0, sizeof(segment->cmd)); segment->cmd.cmd = LC_SEGMENT; strncpy(segment->cmd.segname, segname, sizeof(segment->cmd.segname)); segment->cmd.maxprot = maxprot; segment->cmd.initprot = initprot; segment->cmd.flags = flags; segment->firstSection = segment->lastSection = NULL; segment->next = NULL; if (FirstSeg == NULL) FirstSeg = segment; else LastSeg->next = segment; LastSeg = segment; return segment; } MachOSection *MachO_CreateSection(MachOSegment *segment, char *segname, char *sectname, UInt32 align, UInt32 flags, UInt32 sectionID) { MachOSection *section; UInt32 alignConv; alignConv = 0; while (!(align & 1)) { align >>= 1; alignConv++; } section = galloc(sizeof(MachOSection)); memset(section, 0, sizeof(MachOSection)); strncpy(section->section.segname, segname, sizeof(section->section.segname)); strncpy(section->section.sectname, sectname, sizeof(section->section.sectname)); section->section.align = alignConv; section->section.flags = flags; section->num = ++SectNum; section->id = sectionID; section->next = NULL; if (segment->firstSection == NULL) segment->firstSection = section; else segment->lastSection->next = section; segment->lastSection = section; segment->cmd.nsects++; return section; } GList *MachO_GetGList(MachOSection *section) { if (!section->glist.data) InitGList(§ion->glist, 256); return §ion->glist; } void MachO_SetSectionSize(void) { // empty, unused, unknown args } void MachO_Relocate(MachOSection *section, UInt32 address, UInt32 value, char length, char is_pcrel, Boolean is_extern, int reltype) { MachOReloc *reloc; reloc = galloc(sizeof(MachOReloc)); reloc->address = address; reloc->value = value; reloc->length = length; reloc->is_pcrel = is_pcrel; reloc->is_extern = is_extern; reloc->reltype = reltype; reloc->next = NULL; if (section->firstReloc == NULL) section->firstReloc = reloc; else section->lastReloc->next = reloc; section->lastReloc = reloc; section->section.nreloc++; } static SInt32 DeclareString(char *str) { SInt32 offset; if (str) { offset = StringTable.size; AppendGListID(&StringTable, str); } else { offset = 0; } return offset; } UInt32 MachO_DeclareSymbol(char *name, MachOSection *section, UInt32 value, Boolean isAbsolute, short type, short desc) { MachOSymbol *symbol; symbol = galloc(sizeof(MachOSymbol)); memset(symbol, 0, sizeof(MachOSymbol)); if (section) { if (type) { if (nextdefsym == 0) iextdefsym = SymNum; nextdefsym++; } else { if (nlocalsym == 0) ilocalsym = SymNum; nlocalsym++; } symbol->data.type = type | N_SECT; symbol->data.section = section; } else if (isAbsolute) { symbol->data.type = type | N_ABS; } else { if (nundefsym == 0) iundefsym = SymNum; nundefsym++; symbol->data.type = type & ~N_PEXT; } symbol->data.value = value; symbol->data.u.strx = DeclareString(name); symbol->data.desc = desc; symbol->index = SymNum++; if (FirstSym == NULL) FirstSym = symbol; else LastSym->next = symbol; LastSym = symbol; return symbol->index; } void MachO_ReOrderSections(void) { MachOSection *section; MachOSegment *segment; MachOSection *prev; MachOSection *firstRemoved; MachOSection *lastRemoved; UInt32 counter; counter = 0; for (segment = FirstSeg; segment; segment = segment->next) { prev = NULL; section = segment->firstSection; firstRemoved = lastRemoved = NULL; while (section) { if (section->section.size && section->glist.data == NULL && section != segment->firstSection) { // detach this section if (prev) prev->next = section->next; else segment->firstSection = section->next; if (section == segment->lastSection) segment->lastSection = prev; section->next = NULL; // add it to the list to be pushed to the end if (firstRemoved == NULL) firstRemoved = section; else lastRemoved->next = section; lastRemoved = section; // continue iterating if (prev) section = prev->next; else section = segment->firstSection; } else { prev = section; section = section->next; } } // attach the other sections if (firstRemoved) { if (segment->firstSection == NULL) segment->firstSection = firstRemoved; else segment->lastSection->next = firstRemoved; segment->lastSection = lastRemoved; } // renumber them all for (section = segment->firstSection; section; section = section->next) section->num = ++counter; } } void MachO_AddIndirectSymbol(SInt32 symbol) { CError_ASSERT(1577, symbol >= 0); AppendGListLong(&IndirectSymbolTable, symbol); } UInt32 MachO_NumIndirectSym(void) { return IndirectSymbolTable.size / 4; } SInt32 MachO_OutputStab(SymbolData *data, SInt32 strIndex) { MachOSymbol *symbol; symbol = galloc(sizeof(MachOSymbol)); memset(symbol, 0, sizeof(MachOSymbol)); if (strIndex == -1) data->u.strx = DeclareString(data->u.name); else data->u.strx = strIndex; memcpy(&symbol->data, data, sizeof(SymbolData)); if (FirstStab == NULL) FirstStab = symbol; else LastStab->next = symbol; LastStab = symbol; NumStabs++; return data->u.strx; }