MWCC/compiler_and_linker/unsorted/MachO.c

879 lines
28 KiB
C
Raw Normal View History

2022-12-29 12:32:55 +00:00
#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, &section->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(&section->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(&section->glist, 256);
return &section->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;
}