package macho import ( "bytes" "encoding/binary" "io" "git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/os/common" ) // MachO is the executable format used on MacOS. type MachO struct { Header Code []byte Data []byte } // New creates a new Mach-O binary. func New(code []byte, data []byte) *MachO { return &MachO{ Header: Header{ Magic: 0xFEEDFACF, Architecture: CPU_X86_64, MicroArchitecture: 3 | 0x80000000, Type: TypeExecute, NumCommands: 4, SizeCommands: 72*3 + 184, Flags: FlagNoUndefs, Reserved: 0, }, Code: code, Data: data, } } // Write writes the Mach-O format to the given writer. func (m *MachO) Write(writer io.Writer) { binary.Write(writer, binary.LittleEndian, &m.Header) binary.Write(writer, binary.LittleEndian, &Segment64{ LoadCommand: LcSegment64, Length: 72, Name: [16]byte{'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E', 'R', 'O'}, Address: 0, SizeInMemory: config.BaseAddress, Offset: 0, SizeInFile: 0, NumSections: 0, Flag: 0, MaxProt: 0, InitProt: 0, }) codeStart := uint64(32 + m.Header.SizeCommands) codeLength := uint64(len(m.Code)) codeEnd := codeStart + codeLength dataPadding := common.Padding(codeEnd, Align) dataStart := codeEnd + dataPadding binary.Write(writer, binary.LittleEndian, &Segment64{ LoadCommand: LcSegment64, Length: 72, Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'}, Address: config.BaseAddress + codeStart, SizeInMemory: codeLength, Offset: 0, SizeInFile: codeLength, NumSections: 0, Flag: 0, MaxProt: ProtReadable | ProtExecutable, InitProt: ProtReadable | ProtExecutable, }) binary.Write(writer, binary.LittleEndian, &Segment64{ LoadCommand: LcSegment64, Length: 72, Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'}, Address: config.BaseAddress + dataStart, SizeInMemory: uint64(len(m.Data)), Offset: dataStart, SizeInFile: uint64(len(m.Data)), NumSections: 0, Flag: 0, MaxProt: ProtReadable, InitProt: ProtReadable, }) binary.Write(writer, binary.LittleEndian, &Thread{ LoadCommand: LcUnixthread, Len: 184, Type: 0x4, }) binary.Write(writer, binary.LittleEndian, []uint32{ 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, config.BaseAddress + uint32(codeStart), 0, 0, 0, 0, 0, 0, 0, 0, 0, }) writer.Write(m.Code) writer.Write(bytes.Repeat([]byte{0}, int(dataPadding))) writer.Write(m.Data) }