106 lines
2.2 KiB
Go
106 lines
2.2 KiB
Go
// mklipo merges two Mach-O binaries into one macOS universal (fat) binary.
|
|
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
fatMagic = 0xCAFEBABE
|
|
cpuTypeX86_64 = 0x01000007
|
|
cpuTypeARM64 = 0x0100000c
|
|
fatArchSize = 20
|
|
)
|
|
|
|
type slice struct {
|
|
cpu, sub uint32
|
|
data []byte
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) != 4 {
|
|
fmt.Fprintln(os.Stderr, "usage: mklipo <amd64-bin> <arm64-bin> <out>")
|
|
os.Exit(2)
|
|
}
|
|
amd, err := os.ReadFile(os.Args[1])
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
arm, err := os.ReadFile(os.Args[2])
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
if err := writeFat(os.Args[3], []slice{
|
|
{cpuTypeX86_64, 3, amd},
|
|
{cpuTypeARM64, 0, arm},
|
|
}); err != nil {
|
|
fatal(err)
|
|
}
|
|
}
|
|
|
|
func writeFat(path string, parts []slice) error {
|
|
const align uint32 = 0x4000
|
|
hdr := 8 + fatArchSize*len(parts)
|
|
offsets := make([]uint32, len(parts))
|
|
cur := uint32(hdr)
|
|
for i, p := range parts {
|
|
pad := (align - (cur % align)) % align
|
|
cur += pad
|
|
offsets[i] = cur
|
|
cur += uint32(len(p.data))
|
|
}
|
|
|
|
out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
if err := binary.Write(out, binary.BigEndian, uint32(fatMagic)); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(out, binary.BigEndian, uint32(len(parts))); err != nil {
|
|
return err
|
|
}
|
|
for i, p := range parts {
|
|
if err := binary.Write(out, binary.BigEndian, p.cpu); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(out, binary.BigEndian, p.sub); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(out, binary.BigEndian, offsets[i]); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(out, binary.BigEndian, uint32(len(p.data))); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(out, binary.BigEndian, align); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cur = uint32(hdr)
|
|
for _, p := range parts {
|
|
pad := (align - (cur % align)) % align
|
|
if pad > 0 {
|
|
if _, err := out.Write(make([]byte, pad)); err != nil {
|
|
return err
|
|
}
|
|
cur += pad
|
|
}
|
|
if _, err := out.Write(p.data); err != nil {
|
|
return err
|
|
}
|
|
cur += uint32(len(p.data))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func fatal(err error) {
|
|
fmt.Fprintln(os.Stderr, "mklipo:", err)
|
|
os.Exit(1)
|
|
}
|