diff options
Diffstat (limited to 'src/cmd/newlink/macho.go')
-rw-r--r-- | src/cmd/newlink/macho.go | 380 |
1 files changed, 0 insertions, 380 deletions
diff --git a/src/cmd/newlink/macho.go b/src/cmd/newlink/macho.go deleted file mode 100644 index 4e5524be9b..0000000000 --- a/src/cmd/newlink/macho.go +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Mach-O (Darwin) object file writing. - -package main - -import ( - "debug/macho" - "encoding/binary" - "io" - "strings" -) - -// machoFormat is the implementation of formatter. -type machoFormat struct{} - -// machoHeader and friends are data structures -// corresponding to the Mach-O file header -// to be written to disk. - -const ( - macho64Bit = 1 << 24 - machoSubCPU386 = 3 -) - -// machoArch describes a Mach-O target architecture. -type machoArch struct { - CPU uint32 - SubCPU uint32 -} - -// machoHeader is the Mach-O file header. -type machoHeader struct { - machoArch - FileType uint32 - Loads []*machoLoad - Segments []*machoSegment - p *Prog // for reporting errors -} - -// machoLoad is a Mach-O load command. -type machoLoad struct { - Type uint32 - Data []uint32 -} - -// machoSegment is a Mach-O segment. -type machoSegment struct { - Name string - VirtAddr Addr - VirtSize Addr - FileOffset Addr - FileSize Addr - Prot1 uint32 - Prot2 uint32 - Flags uint32 - Sections []*machoSection -} - -// machoSection is a Mach-O section, inside a segment. -type machoSection struct { - Name string - Segment string - Addr Addr - Size Addr - Offset uint32 - Align uint32 - Reloc uint32 - Nreloc uint32 - Flags uint32 - Res1 uint32 - Res2 uint32 -} - -// layout positions the segments and sections in p -// to make room for the Mach-O file header. -// That is, it edits their VirtAddr fields to adjust for the presence -// of the Mach-O header at the beginning of the address space. -func (machoFormat) headerSize(p *Prog) (virt, file Addr) { - var h machoHeader - h.init(p) - size := Addr(h.size()) - size = round(size, 4096) - p.HeaderSize = size - return size, size -} - -// write writes p to w as a Mach-O executable. -// layout(p) must have already been called, -// and the number, sizes, and addresses of the segments -// and sections must not have been modified since the call. -func (machoFormat) write(w io.Writer, p *Prog) { - var h machoHeader - h.init(p) - off := Addr(0) - enc := h.encode() - w.Write(enc) - off += Addr(len(enc)) - for _, seg := range p.Segments { - if seg.FileOffset < off { - h.p.errorf("mach-o error: invalid file offset") - } - w.Write(make([]byte, int(seg.FileOffset-off))) - if seg.FileSize != Addr(len(seg.Data)) { - h.p.errorf("mach-o error: invalid file size") - } - w.Write(seg.Data) - off = seg.FileOffset + Addr(len(seg.Data)) - } -} - -// Conversion of Prog to macho data structures. - -// machoArches maps from GOARCH to machoArch. -var machoArches = map[string]machoArch{ - "amd64": { - CPU: uint32(macho.CpuAmd64), - SubCPU: uint32(machoSubCPU386), - }, -} - -// init initializes the header h to describe p. -func (h *machoHeader) init(p *Prog) { - h.p = p - h.Segments = nil - h.Loads = nil - var ok bool - h.machoArch, ok = machoArches[p.GOARCH] - if !ok { - p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH) - return - } - h.FileType = uint32(macho.TypeExec) - - mseg := h.addSegment(p, "__PAGEZERO", nil) - mseg.VirtSize = p.UnmappedSize - - for _, seg := range p.Segments { - h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg) - } - - var data []uint32 - switch h.CPU { - default: - p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH) - case uint32(macho.CpuAmd64): - data = make([]uint32, 2+42) - data[0] = 4 // thread type - data[1] = 42 // word count - data[2+32] = uint32(p.Entry) // RIP register, in two parts - data[2+32+1] = uint32(p.Entry >> 32) - } - - h.Loads = append(h.Loads, &machoLoad{ - Type: uint32(macho.LoadCmdUnixThread), - Data: data, - }) -} - -// addSegment adds to h a Mach-O segment like seg with the given name. -func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment { - mseg := &machoSegment{ - Name: name, - } - h.Segments = append(h.Segments, mseg) - if seg == nil { - return mseg - } - - mseg.VirtAddr = seg.VirtAddr - mseg.VirtSize = seg.VirtSize - mseg.FileOffset = round(seg.FileOffset, 4096) - mseg.FileSize = seg.FileSize - - if name == "__TEXT" { - // Initially RWX, then just RX - mseg.Prot1 = 7 - mseg.Prot2 = 5 - - // Text segment maps Mach-O header, needed by dynamic linker. - mseg.VirtAddr -= p.HeaderSize - mseg.VirtSize += p.HeaderSize - mseg.FileOffset -= p.HeaderSize - mseg.FileSize += p.HeaderSize - } else { - // RW - mseg.Prot1 = 3 - mseg.Prot2 = 3 - } - - for _, sect := range seg.Sections { - h.addSection(mseg, seg, sect) - } - return mseg -} - -// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name. -func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) { - msect := &machoSection{ - Name: "__" + sect.Name, - Segment: mseg.Name, - // Reloc: sect.RelocOffset, - // NumReloc: sect.RelocLen / 8, - Addr: sect.VirtAddr, - Size: sect.Size, - } - mseg.Sections = append(mseg.Sections, msect) - - for 1<<msect.Align < sect.Align { - msect.Align++ - } - - if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize { - // Data in file. - if sect.Size > seg.FileSize-off { - h.p.errorf("mach-o error: section crosses file boundary") - } - msect.Offset = uint32(seg.FileOffset + off) - } else { - // Zero filled. - msect.Flags |= 1 - } - - if sect.Name == "text" { - msect.Flags |= 0x400 // contains executable instructions - } -} - -// A machoWriter helps write Mach-O headers. -// It is basically a buffer with some helper routines for writing integers. -type machoWriter struct { - dst []byte - tmp [8]byte - order binary.ByteOrder - is64 bool - p *Prog -} - -// if64 returns x if w is writing a 64-bit object file; otherwise it returns y. -func (w *machoWriter) if64(x, y interface{}) interface{} { - if w.is64 { - return x - } - return y -} - -// encode encodes each of the given arguments into the writer. -// It encodes uint32, []uint32, uint64, and []uint64 by writing each value -// in turn in the correct byte order for the output file. -// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32. -// It encodes []byte and string by writing the raw bytes (no length prefix). -// It skips nil values in the args list. -func (w *machoWriter) encode(args ...interface{}) { - for _, arg := range args { - switch arg := arg.(type) { - default: - w.p.errorf("mach-o error: cannot encode %T", arg) - case nil: - // skip - case []byte: - w.dst = append(w.dst, arg...) - case string: - w.dst = append(w.dst, arg...) - case uint32: - w.order.PutUint32(w.tmp[:], arg) - w.dst = append(w.dst, w.tmp[:4]...) - case []uint32: - for _, x := range arg { - w.order.PutUint32(w.tmp[:], x) - w.dst = append(w.dst, w.tmp[:4]...) - } - case uint64: - w.order.PutUint64(w.tmp[:], arg) - w.dst = append(w.dst, w.tmp[:8]...) - case Addr: - if w.is64 { - w.order.PutUint64(w.tmp[:], uint64(arg)) - w.dst = append(w.dst, w.tmp[:8]...) - } else { - if Addr(uint32(arg)) != arg { - w.p.errorf("mach-o error: truncating address %#x to uint32", arg) - } - w.order.PutUint32(w.tmp[:], uint32(arg)) - w.dst = append(w.dst, w.tmp[:4]...) - } - } - } -} - -// segmentSize returns the size of the encoding of seg in bytes. -func (w *machoWriter) segmentSize(seg *machoSegment) int { - if w.is64 { - return 18*4 + 20*4*len(seg.Sections) - } - return 14*4 + 22*4*len(seg.Sections) -} - -// zeroPad returns the string s truncated or padded with NULs to n bytes. -func zeroPad(s string, n int) string { - if len(s) >= n { - return s[:n] - } - return s + strings.Repeat("\x00", n-len(s)) -} - -// size returns the encoded size of the header. -func (h *machoHeader) size() int { - // Could write separate code, but encoding is cheap; encode and throw it away. - return len(h.encode()) -} - -// encode returns the Mach-O encoding of the header. -func (h *machoHeader) encode() []byte { - w := &machoWriter{p: h.p} - w.is64 = h.CPU&macho64Bit != 0 - w.order = w.p.byteorder - - loadSize := 0 - for _, seg := range h.Segments { - loadSize += w.segmentSize(seg) - } - for _, l := range h.Loads { - loadSize += 4 * (2 + len(l.Data)) - } - - w.encode( - w.if64(macho.Magic64, macho.Magic32), - uint32(h.CPU), - uint32(h.SubCPU), - uint32(h.FileType), - uint32(len(h.Loads)+len(h.Segments)), - uint32(loadSize), - uint32(1), - w.if64(uint32(0), nil), - ) - - for _, seg := range h.Segments { - w.encode( - w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)), - uint32(w.segmentSize(seg)), - zeroPad(seg.Name, 16), - seg.VirtAddr, - seg.VirtSize, - seg.FileOffset, - seg.FileSize, - seg.Prot1, - seg.Prot2, - uint32(len(seg.Sections)), - seg.Flags, - ) - for _, sect := range seg.Sections { - w.encode( - zeroPad(sect.Name, 16), - zeroPad(seg.Name, 16), - sect.Addr, - sect.Size, - sect.Offset, - sect.Align, - sect.Reloc, - sect.Nreloc, - sect.Flags, - sect.Res1, - sect.Res2, - w.if64(uint32(0), nil), - ) - } - } - - for _, load := range h.Loads { - w.encode( - load.Type, - uint32(4*(2+len(load.Data))), - load.Data, - ) - } - - return w.dst -} |