Files
navi/tools/mklipo/main.go
T

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)
}