Updated Mach-O format to use LC_MAIN
All checks were successful
/ test (push) Successful in 17s

This commit is contained in:
Eduard Urbach 2025-07-08 19:17:48 +02:00
parent 8b5cca921b
commit e3444b3626
Signed by: ed
GPG key ID: 49226B848C78F6C8
8 changed files with 119 additions and 41 deletions

View file

@ -2,7 +2,8 @@
## Notes
MacOS requires including the headers in the __TEXT segment.
- Headers must be included in the __TEXT segment.
- Load command size must be divisible by 8.
## Links

View file

@ -2,6 +2,6 @@ package macho
const (
BaseAddress = 0x1000000
SizeCommands = Segment64Size*3 + ThreadSize
SizeCommands = Segment64Size*4 + DyldInfoCommandSize + MainSize + DylinkerCommandSize + len(LinkerString) + DylibCommandSize + len(LibSystemString)
HeaderEnd = HeaderSize + SizeCommands
)

View file

@ -0,0 +1,19 @@
package macho
const DyldInfoCommandSize = 48
// DyldInfoCommand contains the file offsets and sizes that dyld needs to load the image.
type DyldInfoCommand struct {
LoadCommand
Length uint32
RebaseOffset uint32
RebaseSize uint32
BindOffset uint32
BindSize uint32
WeakBindOffset uint32
WeakBindSize uint32
LazyBindOffset uint32
LazyBindSize uint32
ExportOffset uint32
ExportSize uint32
}

16
src/macho/DylibCommand.go Normal file
View file

@ -0,0 +1,16 @@
package macho
const (
DylibCommandSize = 24
LibSystemString = "/usr/lib/libSystem.B.dylib\000\000\000\000\000\000"
)
// DylibCommand is added for each shared library.
type DylibCommand struct {
LoadCommand
Length uint32
Name uint32
TimeStamp uint32
CurrentVersion uint32
CompatibilityVersion uint32
}

View file

@ -0,0 +1,13 @@
package macho
const (
DylinkerCommandSize = 12
LinkerString = "/usr/lib/dyld\000\000\000\000\000\000\000"
)
// DylinkerCommand is needed if the program uses a dynamic linker.
type DylinkerCommand struct {
LoadCommand
Length uint32
Name uint32
}

View file

@ -8,7 +8,7 @@ const (
LcThread LoadCommand = 0x4
LcUnixthread LoadCommand = 0x5
LcDysymtab LoadCommand = 0xB
LcDylib LoadCommand = 0xC
LcLoadDylib LoadCommand = 0xC
LcIdDylib LoadCommand = 0xD
LcLoadDylinker LoadCommand = 0xE
LcIdDylinker LoadCommand = 0xF

View file

@ -14,16 +14,20 @@ type MachO struct {
PageZero Segment64
CodeHeader Segment64
DataHeader Segment64
UnixThread Thread
ImportsHeader Segment64
MainHeader Main
InfoHeader DyldInfoCommand
LinkerHeader DylinkerCommand
LibSystemHeader DylibCommand
}
// 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(), b.Congruent(), codeBytes, dataBytes)
x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign(), b.Congruent(), codeBytes, dataBytes, nil)
code := x.Sections[0]
data := x.Sections[1]
imports := x.Sections[2]
arch, microArch := Arch(b.Arch)
entryPoint := BaseAddress + code.MemoryOffset
m := &MachO{
Header: Header{
@ -31,8 +35,8 @@ func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []
Architecture: arch,
MicroArchitecture: microArch,
Type: TypeExecute,
NumCommands: 4,
SizeCommands: SizeCommands,
NumCommands: 8,
SizeCommands: uint32(SizeCommands),
Flags: FlagNoUndefs | FlagPIE | FlagNoHeapExecution,
Reserved: 0,
},
@ -65,7 +69,7 @@ func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []
DataHeader: Segment64{
LoadCommand: LcSegment64,
Length: Segment64Size,
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A', '_', 'C', 'O', 'N', 'S', 'T'},
Address: uint64(BaseAddress + data.MemoryOffset),
SizeInMemory: uint64(len(data.Bytes)),
Offset: uint64(data.FileOffset),
@ -75,34 +79,40 @@ func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []
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,
ImportsHeader: Segment64{
LoadCommand: LcSegment64,
Length: Segment64Size,
Name: [16]byte{'_', '_', 'L', 'I', 'N', 'K', 'E', 'D', 'I', 'T'},
Address: uint64(BaseAddress + imports.MemoryOffset),
SizeInMemory: uint64(len(imports.Bytes)),
Offset: uint64(imports.FileOffset),
SizeInFile: uint64(len(imports.Bytes)),
NumSections: 0,
Flag: 0,
MaxProt: ProtReadable,
InitProt: ProtReadable,
},
MainHeader: Main{
LoadCommand: LcMain,
Length: MainSize,
EntryFileOffset: uint64(code.MemoryOffset),
StackSize: 0,
},
InfoHeader: DyldInfoCommand{
LoadCommand: LcDyldInfoOnly,
Length: DyldInfoCommandSize,
RebaseOffset: uint32(imports.FileOffset),
RebaseSize: uint32(len(imports.Bytes)),
},
LinkerHeader: DylinkerCommand{
LoadCommand: LcLoadDylinker,
Length: uint32(DylinkerCommandSize + len(LinkerString)),
Name: DylinkerCommandSize,
},
LibSystemHeader: DylibCommand{
LoadCommand: LcLoadDylib,
Length: uint32(DylibCommandSize + len(LibSystemString)),
Name: DylibCommandSize,
},
}
@ -110,9 +120,17 @@ func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []
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)
binary.Write(writer, binary.LittleEndian, &m.ImportsHeader)
binary.Write(writer, binary.LittleEndian, &m.MainHeader)
binary.Write(writer, binary.LittleEndian, &m.InfoHeader)
binary.Write(writer, binary.LittleEndian, &m.LinkerHeader)
writer.Write([]byte(LinkerString))
binary.Write(writer, binary.LittleEndian, &m.LibSystemHeader)
writer.Write([]byte(LibSystemString))
writer.Seek(int64(code.Padding), io.SeekCurrent)
writer.Write(code.Bytes)
writer.Seek(int64(data.Padding), io.SeekCurrent)
writer.Write(data.Bytes)
writer.Seek(int64(imports.Padding), io.SeekCurrent)
writer.Write(imports.Bytes)
}

11
src/macho/Main.go Normal file
View file

@ -0,0 +1,11 @@
package macho
const MainSize = 24
// Main is the structure of the LC_MAIN load command.
type Main struct {
LoadCommand
Length uint32
EntryFileOffset uint64
StackSize uint64
}