macho/macho-go/pkg/ios/fat/create-fat.go

185 lines
4.3 KiB
Go
Raw Permalink Normal View History

2023-05-31 16:31:52 +07:00
// / Contains the declaration of FatHeader and FatArch
// / These structs are always written using Big-Endian,
// / as documented in the mach-o/fat.h
// / This file also has a CreateFat function to generate
// / Fat file from a list of MachoContext
2023-05-31 16:17:03 +07:00
package fat
import (
"io"
"os"
"sort"
log "github.com/sirupsen/logrus"
. "ios-wrapper/pkg/ios"
macho "ios-wrapper/pkg/ios/macho"
)
2023-05-31 16:31:52 +07:00
// / Get the alignment for the Mach-O in Fat binary
// / The returned value is the multiplier of 2
2023-05-31 16:17:03 +07:00
func GetAlignment(h *macho.Header) uint32 {
switch h.Cputype() {
case CPU_TYPE_ARM, CPU_TYPE_ARM64:
return 0xe // log2(0x4000)
case CPU_TYPE_POWERPC,
CPU_TYPE_POWERPC64,
CPU_TYPE_I386,
CPU_TYPE_X86_64:
return 0xc // log2(0x1000)
default:
return 0xd // log2(0x2000)
}
}
func MachosToFatArchs(machos []*macho.MachoContext) []*FatArch {
var fa []*FatArch
for _, m := range machos {
fa = append(fa, &FatArch{
Cputype: m.Header().Cputype(),
Cpusubtype: m.Header().Cpusubtype(),
Size: m.FileSize(),
Offset: 0,
Align: GetAlignment(m.Header()),
})
}
return fa
}
2023-05-31 16:31:52 +07:00
// / Create a Fat binary from MachoContext
// / Convert MachoContext to FatArch
// / Calculate the alignment, now using basic calculation
// / because iOS always has valid archs ARM
// /
// / Sort the Fat arch as per the cctools/lipo
// / Calculate the offset with each Mach-O
// /
// / Write the FatHeader
// / Write the FatArchs
// / Write the Mach-Os
2023-05-31 16:17:03 +07:00
func CreateFat(machos []*macho.MachoContext, outfilename string) error {
archs := MachosToFatArchs(machos)
sort.SliceStable(archs, func(i, j int) bool {
if archs[i].Cputype == archs[j].Cputype {
return archs[i].Cpusubtype < archs[i].Cpusubtype
}
if archs[i].Cputype == CPU_TYPE_ARM64 {
return false
}
if archs[j].Cputype == CPU_TYPE_ARM64 {
return true
}
return archs[i].Cpusubtype < archs[j].Cpusubtype
})
// calculate the offset for each FatArch
offset := uint32(8) // size of fat header, bytes
// offset to the first macho
offset += uint32(len(archs)) * 5 // size of fat_arch, bytes
for _, arch := range archs {
offset = uint32(OffsetRounder(uint64(offset), 1<<arch.Align))
arch.Offset = offset
offset += arch.Size
log.WithFields(log.Fields{
"cputype": arch.Cputype,
"cpusubtype": arch.Cpusubtype,
"size": arch.Size,
"offset": arch.Offset,
"algin": arch.Align,
}).Debug("Arch to add")
}
file, err := os.OpenFile(outfilename, os.O_CREATE|os.O_WRONLY, 0777)
if err != nil {
return err
// log.Fatal("Cannot open output file")
}
var w fatWriter
w.WriteFatHeader(file, &FatHeader{
Magic: MagicFat,
Nfat_arch: uint32(len(archs)),
})
w.WriteFatArchs(file, archs)
w.WriteMachos(file, archs, machos)
return w.err
}
type fatWriter struct {
err error
}
func (fw *fatWriter) WriteFatHeader(w io.Writer, h *FatHeader) {
if fw.err != nil {
return
}
b := h.Serialize()
_, err := w.Write(b)
fw.err = err
}
func (fw *fatWriter) WriteFatArchs(w io.Writer, archs []*FatArch) {
for _, arch := range archs {
if fw.err != nil {
return
}
b := arch.Serialize()
_, err := w.Write(b)
fw.err = err
log.WithFields(log.Fields{
"cputype": arch.Cputype,
"cpusubtype": arch.Cpusubtype,
"size": arch.Size,
"offset": arch.Offset,
"algin": arch.Align,
}).Info("Attempt to write Fat Arch")
}
}
2023-05-31 16:31:52 +07:00
// / for each fat arch sorted, we locate the MachoContext and
// / use it to Write to the buffer
// / locating the macho by its cputype and cpusubtype
2023-05-31 16:17:03 +07:00
func (fw *fatWriter) WriteMachos(
w io.WriteSeeker,
archs []*FatArch,
machos []*macho.MachoContext,
) {
for _, arch := range archs {
for _, macho := range machos {
if fw.err != nil {
return
}
cputype := macho.Header().Cputype()
cpusubtype := macho.Header().Cpusubtype()
if cputype == arch.Cputype &&
cpusubtype == arch.Cpusubtype {
log.WithFields(log.Fields{
"cputype": cputype,
"cpusubtype": cpusubtype,
"offset": arch.Offset,
"size": arch.Size,
}).Info("Attempt to write Mach-O to file")
_, err1 := w.Seek(int64(arch.Offset), io.SeekStart)
_, err2 := macho.WriteBufferTo(w)
if err1 != nil {
fw.err = err1
} else if err2 != nil {
fw.err = err2
}
}
}
}
}
func OffsetRounder(v, r uint64) uint64 {
r--
v += r
v &= uint64(^int64(r))
return v
}