github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/cmd/link/macho.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Mach-O (Darwin) object file writing. 6 7 package main 8 9 import ( 10 "debug/macho" 11 "encoding/binary" 12 "io" 13 "strings" 14 ) 15 16 // machoFormat is the implementation of formatter. 17 type machoFormat struct{} 18 19 // machoHeader and friends are data structures 20 // corresponding to the Mach-O file header 21 // to be written to disk. 22 23 const ( 24 macho64Bit = 1 << 24 25 machoSubCPU386 = 3 26 ) 27 28 // machoArch describes a Mach-O target architecture. 29 type machoArch struct { 30 CPU uint32 31 SubCPU uint32 32 } 33 34 // machoHeader is the Mach-O file header. 35 type machoHeader struct { 36 machoArch 37 FileType uint32 38 Loads []*machoLoad 39 Segments []*machoSegment 40 p *Prog // for reporting errors 41 } 42 43 // machoLoad is a Mach-O load command. 44 type machoLoad struct { 45 Type uint32 46 Data []uint32 47 } 48 49 // machoSegment is a Mach-O segment. 50 type machoSegment struct { 51 Name string 52 VirtAddr Addr 53 VirtSize Addr 54 FileOffset Addr 55 FileSize Addr 56 Prot1 uint32 57 Prot2 uint32 58 Flags uint32 59 Sections []*machoSection 60 } 61 62 // machoSection is a Mach-O section, inside a segment. 63 type machoSection struct { 64 Name string 65 Segment string 66 Addr Addr 67 Size Addr 68 Offset uint32 69 Align uint32 70 Reloc uint32 71 Nreloc uint32 72 Flags uint32 73 Res1 uint32 74 Res2 uint32 75 } 76 77 // layout positions the segments and sections in p 78 // to make room for the Mach-O file header. 79 // That is, it edits their VirtAddr fields to adjust for the presence 80 // of the Mach-O header at the beginning of the address space. 81 func (machoFormat) headerSize(p *Prog) (virt, file Addr) { 82 var h machoHeader 83 h.init(p) 84 size := Addr(h.size()) 85 size = round(size, 4096) 86 p.HeaderSize = size 87 return size, size 88 } 89 90 // write writes p to w as a Mach-O executable. 91 // layout(p) must have already been called, 92 // and the number, sizes, and addresses of the segments 93 // and sections must not have been modified since the call. 94 func (machoFormat) write(w io.Writer, p *Prog) { 95 var h machoHeader 96 h.init(p) 97 off := Addr(0) 98 enc := h.encode() 99 w.Write(enc) 100 off += Addr(len(enc)) 101 for _, seg := range p.Segments { 102 if seg.FileOffset < off { 103 h.p.errorf("mach-o error: invalid file offset") 104 } 105 w.Write(make([]byte, int(seg.FileOffset-off))) 106 if seg.FileSize != Addr(len(seg.Data)) { 107 h.p.errorf("mach-o error: invalid file size") 108 } 109 w.Write(seg.Data) 110 off = seg.FileOffset + Addr(len(seg.Data)) 111 } 112 } 113 114 // Conversion of Prog to macho data structures. 115 116 // machoArches maps from GOARCH to machoArch. 117 var machoArches = map[string]machoArch{ 118 "amd64": { 119 CPU: uint32(macho.CpuAmd64), 120 SubCPU: uint32(machoSubCPU386), 121 }, 122 } 123 124 // init initializes the header h to describe p. 125 func (h *machoHeader) init(p *Prog) { 126 h.p = p 127 h.Segments = nil 128 h.Loads = nil 129 var ok bool 130 h.machoArch, ok = machoArches[p.GOARCH] 131 if !ok { 132 p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH) 133 return 134 } 135 h.FileType = uint32(macho.TypeExec) 136 137 mseg := h.addSegment(p, "__PAGEZERO", nil) 138 mseg.VirtSize = p.UnmappedSize 139 140 for _, seg := range p.Segments { 141 h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg) 142 } 143 144 var data []uint32 145 switch h.CPU { 146 default: 147 p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH) 148 case uint32(macho.CpuAmd64): 149 data = make([]uint32, 2+42) 150 data[0] = 4 // thread type 151 data[1] = 42 // word count 152 data[2+32] = uint32(p.Entry) // RIP register, in two parts 153 data[2+32+1] = uint32(p.Entry >> 32) 154 } 155 156 h.Loads = append(h.Loads, &machoLoad{ 157 Type: uint32(macho.LoadCmdUnixThread), 158 Data: data, 159 }) 160 } 161 162 // addSegment adds to h a Mach-O segment like seg with the given name. 163 func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment { 164 mseg := &machoSegment{ 165 Name: name, 166 } 167 h.Segments = append(h.Segments, mseg) 168 if seg == nil { 169 return mseg 170 } 171 172 mseg.VirtAddr = seg.VirtAddr 173 mseg.VirtSize = seg.VirtSize 174 mseg.FileOffset = round(seg.FileOffset, 4096) 175 mseg.FileSize = seg.FileSize 176 177 if name == "__TEXT" { 178 // Initially RWX, then just RX 179 mseg.Prot1 = 7 180 mseg.Prot2 = 5 181 182 // Text segment maps Mach-O header, needed by dynamic linker. 183 mseg.VirtAddr -= p.HeaderSize 184 mseg.VirtSize += p.HeaderSize 185 mseg.FileOffset -= p.HeaderSize 186 mseg.FileSize += p.HeaderSize 187 } else { 188 // RW 189 mseg.Prot1 = 3 190 mseg.Prot2 = 3 191 } 192 193 for _, sect := range seg.Sections { 194 h.addSection(mseg, seg, sect) 195 } 196 return mseg 197 } 198 199 // addSection adds to mseg a Mach-O section like sect, inside seg, with the given name. 200 func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) { 201 msect := &machoSection{ 202 Name: "__" + sect.Name, 203 Segment: mseg.Name, 204 // Reloc: sect.RelocOffset, 205 // NumReloc: sect.RelocLen / 8, 206 Addr: sect.VirtAddr, 207 Size: sect.Size, 208 } 209 mseg.Sections = append(mseg.Sections, msect) 210 211 for 1<<msect.Align < sect.Align { 212 msect.Align++ 213 } 214 215 if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize { 216 // Data in file. 217 if sect.Size > seg.FileSize-off { 218 h.p.errorf("mach-o error: section crosses file boundary") 219 } 220 msect.Offset = uint32(seg.FileOffset + off) 221 } else { 222 // Zero filled. 223 msect.Flags |= 1 224 } 225 226 if sect.Name == "text" { 227 msect.Flags |= 0x400 // contains executable instructions 228 } 229 } 230 231 // A machoWriter helps write Mach-O headers. 232 // It is basically a buffer with some helper routines for writing integers. 233 type machoWriter struct { 234 dst []byte 235 tmp [8]byte 236 order binary.ByteOrder 237 is64 bool 238 p *Prog 239 } 240 241 // if64 returns x if w is writing a 64-bit object file; otherwise it returns y. 242 func (w *machoWriter) if64(x, y interface{}) interface{} { 243 if w.is64 { 244 return x 245 } 246 return y 247 } 248 249 // encode encodes each of the given arguments into the writer. 250 // It encodes uint32, []uint32, uint64, and []uint64 by writing each value 251 // in turn in the correct byte order for the output file. 252 // It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32. 253 // It encodes []byte and string by writing the raw bytes (no length prefix). 254 // It skips nil values in the args list. 255 func (w *machoWriter) encode(args ...interface{}) { 256 for _, arg := range args { 257 switch arg := arg.(type) { 258 default: 259 w.p.errorf("mach-o error: cannot encode %T", arg) 260 case nil: 261 // skip 262 case []byte: 263 w.dst = append(w.dst, arg...) 264 case string: 265 w.dst = append(w.dst, arg...) 266 case uint32: 267 w.order.PutUint32(w.tmp[:], arg) 268 w.dst = append(w.dst, w.tmp[:4]...) 269 case []uint32: 270 for _, x := range arg { 271 w.order.PutUint32(w.tmp[:], x) 272 w.dst = append(w.dst, w.tmp[:4]...) 273 } 274 case uint64: 275 w.order.PutUint64(w.tmp[:], arg) 276 w.dst = append(w.dst, w.tmp[:8]...) 277 case Addr: 278 if w.is64 { 279 w.order.PutUint64(w.tmp[:], uint64(arg)) 280 w.dst = append(w.dst, w.tmp[:8]...) 281 } else { 282 if Addr(uint32(arg)) != arg { 283 w.p.errorf("mach-o error: truncating address %#x to uint32", arg) 284 } 285 w.order.PutUint32(w.tmp[:], uint32(arg)) 286 w.dst = append(w.dst, w.tmp[:4]...) 287 } 288 } 289 } 290 } 291 292 // segmentSize returns the size of the encoding of seg in bytes. 293 func (w *machoWriter) segmentSize(seg *machoSegment) int { 294 if w.is64 { 295 return 18*4 + 20*4*len(seg.Sections) 296 } 297 return 14*4 + 22*4*len(seg.Sections) 298 } 299 300 // zeroPad returns the string s truncated or padded with NULs to n bytes. 301 func zeroPad(s string, n int) string { 302 if len(s) >= n { 303 return s[:n] 304 } 305 return s + strings.Repeat("\x00", n-len(s)) 306 } 307 308 // size returns the encoded size of the header. 309 func (h *machoHeader) size() int { 310 // Could write separate code, but encoding is cheap; encode and throw it away. 311 return len(h.encode()) 312 } 313 314 // encode returns the Mach-O encoding of the header. 315 func (h *machoHeader) encode() []byte { 316 w := &machoWriter{p: h.p} 317 w.is64 = h.CPU&macho64Bit != 0 318 w.order = w.p.byteorder 319 320 loadSize := 0 321 for _, seg := range h.Segments { 322 loadSize += w.segmentSize(seg) 323 } 324 for _, l := range h.Loads { 325 loadSize += 4 * (2 + len(l.Data)) 326 } 327 328 w.encode( 329 w.if64(macho.Magic64, macho.Magic32), 330 uint32(h.CPU), 331 uint32(h.SubCPU), 332 uint32(h.FileType), 333 uint32(len(h.Loads)+len(h.Segments)), 334 uint32(loadSize), 335 uint32(1), 336 w.if64(uint32(0), nil), 337 ) 338 339 for _, seg := range h.Segments { 340 w.encode( 341 w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)), 342 uint32(w.segmentSize(seg)), 343 zeroPad(seg.Name, 16), 344 seg.VirtAddr, 345 seg.VirtSize, 346 seg.FileOffset, 347 seg.FileSize, 348 seg.Prot1, 349 seg.Prot2, 350 uint32(len(seg.Sections)), 351 seg.Flags, 352 ) 353 for _, sect := range seg.Sections { 354 w.encode( 355 zeroPad(sect.Name, 16), 356 zeroPad(seg.Name, 16), 357 sect.Addr, 358 sect.Size, 359 sect.Offset, 360 sect.Align, 361 sect.Reloc, 362 sect.Nreloc, 363 sect.Flags, 364 sect.Res1, 365 sect.Res2, 366 w.if64(uint32(0), nil), 367 ) 368 } 369 } 370 371 for _, load := range h.Loads { 372 w.encode( 373 load.Type, 374 uint32(4*(2+len(load.Data))), 375 load.Data, 376 ) 377 } 378 379 return w.dst 380 }