This commit is contained in:
parent
436691ae40
commit
dbc865ee67
14 changed files with 317 additions and 0 deletions
12
docs/macho.md
Normal file
12
docs/macho.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Mach-O
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
MacOS requires including the headers in the __TEXT segment.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
|
||||||
|
- https://en.wikipedia.org/wiki/Mach-O
|
||||||
|
- https://github.com/aidansteele/osx-abi-macho-file-format-reference
|
||||||
|
- https://stackoverflow.com/questions/39863112/what-is-required-for-a-mach-o-executable-to-load
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"git.urbach.dev/cli/q/src/core"
|
"git.urbach.dev/cli/q/src/core"
|
||||||
"git.urbach.dev/cli/q/src/data"
|
"git.urbach.dev/cli/q/src/data"
|
||||||
"git.urbach.dev/cli/q/src/elf"
|
"git.urbach.dev/cli/q/src/elf"
|
||||||
|
"git.urbach.dev/cli/q/src/macho"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteFile writes an executable file to disk.
|
// WriteFile writes an executable file to disk.
|
||||||
|
@ -37,6 +38,8 @@ func WriteFile(executable string, b *build.Build, env *core.Environment) error {
|
||||||
switch b.OS {
|
switch b.OS {
|
||||||
case build.Linux:
|
case build.Linux:
|
||||||
elf.Write(file, b, code, data)
|
elf.Write(file, b, code, data)
|
||||||
|
case build.Mac:
|
||||||
|
macho.Write(file, b, code, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Close()
|
err = file.Close()
|
||||||
|
|
15
src/macho/Arch.go
Normal file
15
src/macho/Arch.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/build"
|
||||||
|
|
||||||
|
// Arch returns the CPU architecture used in the Mach-O header.
|
||||||
|
func Arch(arch build.Arch) (CPU, uint32) {
|
||||||
|
switch arch {
|
||||||
|
case build.ARM:
|
||||||
|
return CPU_ARM_64, CPU_SUBTYPE_ARM64_ALL | 0x80000000
|
||||||
|
case build.X86:
|
||||||
|
return CPU_X86_64, CPU_SUBTYPE_X86_64_ALL | 0x80000000
|
||||||
|
default:
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
}
|
15
src/macho/CPU.go
Normal file
15
src/macho/CPU.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
type CPU uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
CPU_X86 CPU = 7
|
||||||
|
CPU_X86_64 CPU = CPU_X86 | 0x01000000
|
||||||
|
CPU_ARM CPU = 12
|
||||||
|
CPU_ARM_64 CPU = CPU_ARM | 0x01000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CPU_SUBTYPE_ARM64_ALL = 0
|
||||||
|
CPU_SUBTYPE_X86_64_ALL = 3
|
||||||
|
)
|
7
src/macho/Constants.go
Normal file
7
src/macho/Constants.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
const (
|
||||||
|
BaseAddress = 0x1000000
|
||||||
|
SizeCommands = Segment64Size*3 + ThreadSize
|
||||||
|
HeaderEnd = HeaderSize + SizeCommands
|
||||||
|
)
|
15
src/macho/Header.go
Normal file
15
src/macho/Header.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
const HeaderSize = 32
|
||||||
|
|
||||||
|
// Header contains general information.
|
||||||
|
type Header struct {
|
||||||
|
Magic uint32
|
||||||
|
Architecture CPU
|
||||||
|
MicroArchitecture uint32
|
||||||
|
Type HeaderType
|
||||||
|
NumCommands uint32
|
||||||
|
SizeCommands uint32
|
||||||
|
Flags HeaderFlags
|
||||||
|
Reserved uint32
|
||||||
|
}
|
32
src/macho/HeaderFlags.go
Normal file
32
src/macho/HeaderFlags.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
type HeaderFlags uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagNoUndefs HeaderFlags = 0x1
|
||||||
|
FlagIncrLink HeaderFlags = 0x2
|
||||||
|
FlagDyldLink HeaderFlags = 0x4
|
||||||
|
FlagBindAtLoad HeaderFlags = 0x8
|
||||||
|
FlagPrebound HeaderFlags = 0x10
|
||||||
|
FlagSplitSegs HeaderFlags = 0x20
|
||||||
|
FlagLazyInit HeaderFlags = 0x40
|
||||||
|
FlagTwoLevel HeaderFlags = 0x80
|
||||||
|
FlagForceFlat HeaderFlags = 0x100
|
||||||
|
FlagNoMultiDefs HeaderFlags = 0x200
|
||||||
|
FlagNoFixPrebinding HeaderFlags = 0x400
|
||||||
|
FlagPrebindable HeaderFlags = 0x800
|
||||||
|
FlagAllModsBound HeaderFlags = 0x1000
|
||||||
|
FlagSubsectionsViaSymbols HeaderFlags = 0x2000
|
||||||
|
FlagCanonical HeaderFlags = 0x4000
|
||||||
|
FlagWeakDefines HeaderFlags = 0x8000
|
||||||
|
FlagBindsToWeak HeaderFlags = 0x10000
|
||||||
|
FlagAllowStackExecution HeaderFlags = 0x20000
|
||||||
|
FlagRootSafe HeaderFlags = 0x40000
|
||||||
|
FlagSetuidSafe HeaderFlags = 0x80000
|
||||||
|
FlagNoReexportedDylibs HeaderFlags = 0x100000
|
||||||
|
FlagPIE HeaderFlags = 0x200000
|
||||||
|
FlagDeadStrippableDylib HeaderFlags = 0x400000
|
||||||
|
FlagHasTLVDescriptors HeaderFlags = 0x800000
|
||||||
|
FlagNoHeapExecution HeaderFlags = 0x1000000
|
||||||
|
FlagAppExtensionSafe HeaderFlags = 0x2000000
|
||||||
|
)
|
12
src/macho/HeaderType.go
Normal file
12
src/macho/HeaderType.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
type HeaderType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeObject HeaderType = 0x1
|
||||||
|
TypeExecute HeaderType = 0x2
|
||||||
|
TypeCore HeaderType = 0x4
|
||||||
|
TypeDylib HeaderType = 0x6
|
||||||
|
TypeBundle HeaderType = 0x8
|
||||||
|
TypeDsym HeaderType = 0xA
|
||||||
|
)
|
34
src/macho/LoadCommand.go
Normal file
34
src/macho/LoadCommand.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
type LoadCommand uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
LcSegment LoadCommand = 0x1
|
||||||
|
LcSymtab LoadCommand = 0x2
|
||||||
|
LcThread LoadCommand = 0x4
|
||||||
|
LcUnixthread LoadCommand = 0x5
|
||||||
|
LcDysymtab LoadCommand = 0xB
|
||||||
|
LcDylib LoadCommand = 0xC
|
||||||
|
LcIdDylib LoadCommand = 0xD
|
||||||
|
LcLoadDylinker LoadCommand = 0xE
|
||||||
|
LcIdDylinker LoadCommand = 0xF
|
||||||
|
LcSegment64 LoadCommand = 0x19
|
||||||
|
LcUuid LoadCommand = 0x1B
|
||||||
|
LcCodeSignature LoadCommand = 0x1D
|
||||||
|
LcSegmentSplitInfo LoadCommand = 0x1E
|
||||||
|
LcRpath LoadCommand = 0x8000001C
|
||||||
|
LcEncryptionInfo LoadCommand = 0x21
|
||||||
|
LcDyldInfo LoadCommand = 0x22
|
||||||
|
LcDyldInfoOnly LoadCommand = 0x80000022
|
||||||
|
LcVersionMinMacosx LoadCommand = 0x24
|
||||||
|
LcVersionMinIphoneos LoadCommand = 0x25
|
||||||
|
LcFunctionStarts LoadCommand = 0x26
|
||||||
|
LcDyldEnvironment LoadCommand = 0x27
|
||||||
|
LcMain LoadCommand = 0x80000028
|
||||||
|
LcDataInCode LoadCommand = 0x29
|
||||||
|
LcSourceVersion LoadCommand = 0x2A
|
||||||
|
LcDylibCodeSignDrs LoadCommand = 0x2B
|
||||||
|
LcEncryptionInfo64 LoadCommand = 0x2C
|
||||||
|
LcVersionMinTvos LoadCommand = 0x2F
|
||||||
|
LcVersionMinWatchos LoadCommand = 0x30
|
||||||
|
)
|
119
src/macho/MachO.go
Normal file
119
src/macho/MachO.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.urbach.dev/cli/q/src/build"
|
||||||
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MachO is the executable format used on MacOS.
|
||||||
|
type MachO struct {
|
||||||
|
Header
|
||||||
|
PageZero Segment64
|
||||||
|
CodeHeader Segment64
|
||||||
|
DataHeader Segment64
|
||||||
|
UnixThread Thread
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the Mach-O format to the given writer.
|
||||||
|
func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []byte) {
|
||||||
|
x := exe.New(HeaderEnd, b.FileAlign, b.MemoryAlign)
|
||||||
|
x.InitSections(codeBytes, dataBytes)
|
||||||
|
code := x.Sections[0]
|
||||||
|
data := x.Sections[1]
|
||||||
|
arch, microArch := Arch(b.Arch)
|
||||||
|
entryPoint := BaseAddress + code.MemoryOffset
|
||||||
|
|
||||||
|
m := &MachO{
|
||||||
|
Header: Header{
|
||||||
|
Magic: 0xFEEDFACF,
|
||||||
|
Architecture: arch,
|
||||||
|
MicroArchitecture: microArch,
|
||||||
|
Type: TypeExecute,
|
||||||
|
NumCommands: 4,
|
||||||
|
SizeCommands: SizeCommands,
|
||||||
|
Flags: FlagNoUndefs | FlagPIE | FlagNoHeapExecution,
|
||||||
|
Reserved: 0,
|
||||||
|
},
|
||||||
|
PageZero: Segment64{
|
||||||
|
LoadCommand: LcSegment64,
|
||||||
|
Length: 72,
|
||||||
|
Name: [16]byte{'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E', 'R', 'O'},
|
||||||
|
Address: 0,
|
||||||
|
SizeInMemory: uint64(BaseAddress),
|
||||||
|
Offset: 0,
|
||||||
|
SizeInFile: 0,
|
||||||
|
NumSections: 0,
|
||||||
|
Flag: 0,
|
||||||
|
MaxProt: 0,
|
||||||
|
InitProt: 0,
|
||||||
|
},
|
||||||
|
CodeHeader: Segment64{
|
||||||
|
LoadCommand: LcSegment64,
|
||||||
|
Length: Segment64Size,
|
||||||
|
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
|
||||||
|
Address: uint64(BaseAddress),
|
||||||
|
SizeInMemory: uint64(code.MemoryOffset + len(code.Bytes)),
|
||||||
|
Offset: 0,
|
||||||
|
SizeInFile: uint64(code.FileOffset + len(code.Bytes)),
|
||||||
|
NumSections: 0,
|
||||||
|
Flag: 0,
|
||||||
|
MaxProt: ProtReadable | ProtExecutable,
|
||||||
|
InitProt: ProtReadable | ProtExecutable,
|
||||||
|
},
|
||||||
|
DataHeader: Segment64{
|
||||||
|
LoadCommand: LcSegment64,
|
||||||
|
Length: Segment64Size,
|
||||||
|
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
|
||||||
|
Address: uint64(BaseAddress + data.MemoryOffset),
|
||||||
|
SizeInMemory: uint64(len(data.Bytes)),
|
||||||
|
Offset: uint64(data.FileOffset),
|
||||||
|
SizeInFile: uint64(len(data.Bytes)),
|
||||||
|
NumSections: 0,
|
||||||
|
Flag: 0,
|
||||||
|
MaxProt: ProtReadable,
|
||||||
|
InitProt: ProtReadable,
|
||||||
|
},
|
||||||
|
UnixThread: Thread{
|
||||||
|
LoadCommand: LcUnixthread,
|
||||||
|
Len: ThreadSize,
|
||||||
|
Type: 0x4,
|
||||||
|
Data: [43]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,
|
||||||
|
uint32(entryPoint), 0,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.Write(writer, binary.LittleEndian, &m.Header)
|
||||||
|
binary.Write(writer, binary.LittleEndian, &m.PageZero)
|
||||||
|
binary.Write(writer, binary.LittleEndian, &m.CodeHeader)
|
||||||
|
binary.Write(writer, binary.LittleEndian, &m.DataHeader)
|
||||||
|
binary.Write(writer, binary.LittleEndian, &m.UnixThread)
|
||||||
|
writer.Seek(int64(code.Padding), io.SeekCurrent)
|
||||||
|
writer.Write(code.Bytes)
|
||||||
|
writer.Seek(int64(data.Padding), io.SeekCurrent)
|
||||||
|
writer.Write(data.Bytes)
|
||||||
|
}
|
15
src/macho/MachO_test.go
Normal file
15
src/macho/MachO_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package macho_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.urbach.dev/cli/q/src/build"
|
||||||
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
|
"git.urbach.dev/cli/q/src/macho"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWrite(t *testing.T) {
|
||||||
|
macho.Write(&exe.Discard{}, &build.Build{Arch: build.ARM, FileAlign: 0x4000, MemoryAlign: 0x4000}, nil, nil)
|
||||||
|
macho.Write(&exe.Discard{}, &build.Build{Arch: build.X86, FileAlign: 0x1000, MemoryAlign: 0x1000}, nil, nil)
|
||||||
|
macho.Write(&exe.Discard{}, &build.Build{Arch: build.UnknownArch, FileAlign: 0x1000, MemoryAlign: 0x1000}, nil, nil)
|
||||||
|
}
|
9
src/macho/Prot.go
Normal file
9
src/macho/Prot.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
type Prot uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtReadable Prot = 0x1
|
||||||
|
ProtWritable Prot = 0x2
|
||||||
|
ProtExecutable Prot = 0x4
|
||||||
|
)
|
18
src/macho/Segment64.go
Normal file
18
src/macho/Segment64.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
const Segment64Size = 72
|
||||||
|
|
||||||
|
// Segment64 is a segment load command.
|
||||||
|
type Segment64 struct {
|
||||||
|
LoadCommand
|
||||||
|
Length uint32
|
||||||
|
Name [16]byte
|
||||||
|
Address uint64
|
||||||
|
SizeInMemory uint64
|
||||||
|
Offset uint64
|
||||||
|
SizeInFile uint64
|
||||||
|
MaxProt Prot
|
||||||
|
InitProt Prot
|
||||||
|
NumSections uint32
|
||||||
|
Flag uint32
|
||||||
|
}
|
11
src/macho/Thread.go
Normal file
11
src/macho/Thread.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package macho
|
||||||
|
|
||||||
|
const ThreadSize = 184
|
||||||
|
|
||||||
|
// Thread is a thread state load command.
|
||||||
|
type Thread struct {
|
||||||
|
LoadCommand
|
||||||
|
Len uint32
|
||||||
|
Type uint32
|
||||||
|
Data [43]uint32
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue