-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathmakefat.go
More file actions
156 lines (143 loc) · 3.66 KB
/
makefat.go
File metadata and controls
156 lines (143 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main
// makefat <output file> <input file 1> <input file 2> ...
import (
"debug/macho"
"encoding/binary"
"fmt"
"io"
"os"
)
const (
MagicFat64 = macho.MagicFat + 1 // TODO: add to stdlib (...when it works)
// Alignment wanted for each sub-file.
// amd64 needs 12 bits, arm64 needs 14. We choose the max of all requirements here.
alignBits = 14
align = 1 << alignBits
)
func main() {
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "usage: %s <output file> <input file 1> <input file 2> ...\n", os.Args[0])
os.Exit(2)
}
// Read input files.
type input struct {
file *os.File
size int64
cpu uint32
subcpu uint32
offset int64
}
var inputs []input
var head [12]byte
offset := int64(align)
for _, i := range os.Args[2:] {
file, err := os.Open(i)
if err != nil {
panic(err)
}
defer file.Close()
// Read the first 12 bytes of the file and reset the reader
fi, err := file.Stat()
if err != nil {
panic(err)
}
size := fi.Size()
if size < 12 {
panic(fmt.Sprintf("file %s too small", i))
}
n, err := file.Read(head[:])
if n != 12 {
panic(fmt.Sprintf("tried to read 12 bytes but was able to read %d", n))
}
if err != nil {
panic(err)
}
if _, err := file.Seek(0, 0); err != nil {
panic(err)
}
// All currently supported mac archs (386,amd64,arm,arm64) are little endian.
magic := binary.LittleEndian.Uint32(head[0:4])
if magic != macho.Magic32 && magic != macho.Magic64 {
panic(fmt.Sprintf("input %s is not a macho file, magic=%x", i, magic))
}
cpu := binary.LittleEndian.Uint32(head[4:8])
subcpu := binary.LittleEndian.Uint32(head[8:12])
inputs = append(inputs, input{file: file, cpu: cpu, subcpu: subcpu, size: size, offset: offset})
offset += size
offset = (offset + align - 1) / align * align
}
// Decide on whether we're doing fat32 or fat64.
sixtyfour := false
if inputs[len(inputs)-1].offset >= 1<<32 || inputs[len(inputs)-1].size >= 1<<32 {
sixtyfour = true
// fat64 doesn't seem to work:
// - the resulting binary won't run.
// - the resulting binary is parseable by lipo, but reports that the contained files are "hidden".
// - the native OSX lipo can't make a fat64.
panic("files too large to fit into a fat binary")
}
// Make output file.
out, err := os.Create(os.Args[1])
if err != nil {
panic(err)
}
err = out.Chmod(0755)
if err != nil {
panic(err)
}
// Build a fat_header.
var hdr []uint32
if sixtyfour {
hdr = append(hdr, MagicFat64)
} else {
hdr = append(hdr, macho.MagicFat)
}
hdr = append(hdr, uint32(len(inputs)))
// Build a fat_arch for each input file.
for _, i := range inputs {
hdr = append(hdr, i.cpu)
hdr = append(hdr, i.subcpu)
if sixtyfour {
hdr = append(hdr, uint32(i.offset>>32)) // big endian
}
hdr = append(hdr, uint32(i.offset))
if sixtyfour {
hdr = append(hdr, uint32(i.size>>32)) // big endian
}
hdr = append(hdr, uint32(i.size))
hdr = append(hdr, alignBits)
if sixtyfour {
hdr = append(hdr, 0) // reserved
}
}
// Write header.
// Note that the fat binary header is big-endian, regardless of the
// endianness of the contained files.
err = binary.Write(out, binary.BigEndian, hdr)
if err != nil {
panic(err)
}
offset = int64(4 * len(hdr))
// Write each contained file.
for _, i := range inputs {
if offset < i.offset {
_, err = out.Write(make([]byte, i.offset-offset))
if err != nil {
panic(err)
}
offset = i.offset
}
n, err := io.Copy(out, i.file)
if err != nil {
panic(err)
}
if n != i.size {
panic(fmt.Sprintf("expected to copy over %d bytes but copied over %d", i.size, n))
}
offset += int64(i.size)
}
err = out.Close()
if err != nil {
panic(err)
}
}