123 lines
2.6 KiB
Go
123 lines
2.6 KiB
Go
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)
|
|
}
|