github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/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  }