github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/cmd/link/internal/ld/macho_combine_dwarf.go (about)

     1  // Copyright 2015 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  package ld
     6  
     7  import (
     8  	"bytes"
     9  	"debug/macho"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"reflect"
    15  	"unsafe"
    16  )
    17  
    18  var fakedwarf, realdwarf, linkseg *macho.Segment
    19  var dwarfstart, linkstart int64
    20  var linkoffset uint32
    21  var machHeader *macho.FileHeader
    22  var mappedHeader []byte
    23  
    24  const (
    25  	LC_ID_DYLIB             = 0xd
    26  	LC_LOAD_DYLINKER        = 0xe
    27  	LC_PREBOUND_DYLIB       = 0x10
    28  	LC_LOAD_WEAK_DYLIB      = 0x18
    29  	LC_UUID                 = 0x1b
    30  	LC_RPATH                = 0x8000001c
    31  	LC_CODE_SIGNATURE       = 0x1d
    32  	LC_SEGMENT_SPLIT_INFO   = 0x1e
    33  	LC_REEXPORT_DYLIB       = 0x8000001f
    34  	LC_ENCRYPTION_INFO      = 0x21
    35  	LC_DYLD_INFO            = 0x22
    36  	LC_DYLD_INFO_ONLY       = 0x80000022
    37  	LC_VERSION_MIN_MACOSX   = 0x24
    38  	LC_VERSION_MIN_IPHONEOS = 0x25
    39  	LC_FUNCTION_STARTS      = 0x26
    40  	LC_MAIN                 = 0x80000028
    41  	LC_DATA_IN_CODE         = 0x29
    42  	LC_SOURCE_VERSION       = 0x2A
    43  	LC_DYLIB_CODE_SIGN_DRS  = 0x2B
    44  	LC_ENCRYPTION_INFO_64   = 0x2C
    45  
    46  	dwarfMinAlign = 6  // 64 = 1 << 6
    47  	pageAlign     = 12 // 4096 = 1 << 12
    48  )
    49  
    50  type loadCmd struct {
    51  	Cmd macho.LoadCmd
    52  	Len uint32
    53  }
    54  
    55  type dyldInfoCmd struct {
    56  	Cmd                      macho.LoadCmd
    57  	Len                      uint32
    58  	RebaseOff, RebaseLen     uint32
    59  	BindOff, BindLen         uint32
    60  	WeakBindOff, WeakBindLen uint32
    61  	LazyBindOff, LazyBindLen uint32
    62  	ExportOff, ExportLen     uint32
    63  }
    64  
    65  type linkEditDataCmd struct {
    66  	Cmd              macho.LoadCmd
    67  	Len              uint32
    68  	DataOff, DataLen uint32
    69  }
    70  
    71  type encryptionInfoCmd struct {
    72  	Cmd                macho.LoadCmd
    73  	Len                uint32
    74  	CryptOff, CryptLen uint32
    75  	CryptId            uint32
    76  }
    77  
    78  type loadCmdReader struct {
    79  	offset, next int64
    80  	f            *os.File
    81  	order        binary.ByteOrder
    82  }
    83  
    84  func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
    85  	r.offset = r.next
    86  	if _, err = r.f.Seek(r.offset, 0); err != nil {
    87  		return
    88  	}
    89  	if err = binary.Read(r.f, r.order, &cmd); err != nil {
    90  		return
    91  	}
    92  	r.next = r.offset + int64(cmd.Len)
    93  	return
    94  }
    95  
    96  func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
    97  	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
    98  		return err
    99  	}
   100  	return binary.Read(r.f, r.order, data)
   101  }
   102  
   103  func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
   104  	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
   105  		return err
   106  	}
   107  	return binary.Write(r.f, r.order, data)
   108  }
   109  
   110  // machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
   111  // With internal linking, DWARF is embedded into the executable, this lets us do the
   112  // same for external linking.
   113  // inexe is the path to the executable with no DWARF. It must have enough room in the macho
   114  // header to add the DWARF sections. (Use ld's -headerpad option)
   115  // dsym is the path to the macho file containing DWARF from dsymutil.
   116  // outexe is the path where the combined executable should be saved.
   117  func machoCombineDwarf(inexe, dsym, outexe string) error {
   118  	exef, err := os.Open(inexe)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	dwarff, err := os.Open(dsym)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	outf, err := os.Create(outexe)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	outf.Chmod(0755)
   131  
   132  	exem, err := macho.NewFile(exef)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	dwarfm, err := macho.NewFile(dwarff)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// The string table needs to be the last thing in the file
   142  	// for code signing to work. So we'll need to move the
   143  	// linkedit section, but all the others can be copied directly.
   144  	linkseg = exem.Segment("__LINKEDIT")
   145  	if linkseg == nil {
   146  		return fmt.Errorf("missing __LINKEDIT segment")
   147  	}
   148  
   149  	if _, err = exef.Seek(0, 0); err != nil {
   150  		return err
   151  	}
   152  	if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
   153  		return err
   154  	}
   155  
   156  	realdwarf = dwarfm.Segment("__DWARF")
   157  	if realdwarf == nil {
   158  		return fmt.Errorf("missing __DWARF segment")
   159  	}
   160  
   161  	// Now copy the dwarf data into the output.
   162  	maxalign := uint32(dwarfMinAlign) //
   163  	for _, sect := range dwarfm.Sections {
   164  		if sect.Align > maxalign {
   165  			maxalign = sect.Align
   166  		}
   167  	}
   168  	dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
   169  	if _, err = outf.Seek(dwarfstart, 0); err != nil {
   170  		return err
   171  	}
   172  
   173  	if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
   174  		return err
   175  	}
   176  	if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
   177  		return err
   178  	}
   179  
   180  	// And finally the linkedit section.
   181  	if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
   182  		return err
   183  	}
   184  	linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign)
   185  	linkoffset = uint32(linkstart - int64(linkseg.Offset))
   186  	if _, err = outf.Seek(linkstart, 0); err != nil {
   187  		return err
   188  	}
   189  	if _, err := io.Copy(outf, exef); err != nil {
   190  		return err
   191  	}
   192  
   193  	// Now we need to update the headers.
   194  	cmdOffset := unsafe.Sizeof(exem.FileHeader)
   195  	is64bit := exem.Magic == macho.Magic64
   196  	if is64bit {
   197  		// mach_header_64 has one extra uint32.
   198  		cmdOffset += unsafe.Sizeof(exem.Magic)
   199  	}
   200  
   201  	textsect := exem.Section("__text")
   202  	if linkseg == nil {
   203  		return fmt.Errorf("missing __text section")
   204  	}
   205  
   206  	dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
   207  	availablePadding := int64(textsect.Offset) - dwarfCmdOffset
   208  	if availablePadding < int64(realdwarf.Len) {
   209  		return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
   210  	}
   211  	// First, copy the dwarf load command into the header
   212  	if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
   213  		return err
   214  	}
   215  	if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
   216  		return err
   217  	}
   218  
   219  	if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
   220  		return err
   221  	}
   222  	if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
   223  		return err
   224  	}
   225  	if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
   226  		return err
   227  	}
   228  
   229  	reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
   230  	for i := uint32(0); i < exem.Ncmd; i++ {
   231  		cmd, err := reader.Next()
   232  		if err != nil {
   233  			return err
   234  		}
   235  		switch cmd.Cmd {
   236  		case macho.LoadCmdSegment64:
   237  			err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
   238  		case macho.LoadCmdSegment:
   239  			err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
   240  		case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
   241  			err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
   242  		case macho.LoadCmdSymtab:
   243  			err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
   244  		case macho.LoadCmdDysymtab:
   245  			err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
   246  		case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
   247  			err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
   248  		case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
   249  			err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
   250  		case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB:
   251  			// Nothing to update
   252  		default:
   253  			err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
   254  		}
   255  		if err != nil {
   256  			return err
   257  		}
   258  	}
   259  	return machoUpdateDwarfHeader(&reader)
   260  }
   261  
   262  // machoUpdateSegment updates the load command for a moved segment.
   263  // Only the linkedit segment should move, and it should have 0 sections.
   264  // seg should be a macho.Segment32 or macho.Segment64 as appropriate.
   265  // sect should be a macho.Section32 or macho.Section64 as appropriate.
   266  func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
   267  	if err := r.ReadAt(0, seg); err != nil {
   268  		return err
   269  	}
   270  	segValue := reflect.ValueOf(seg)
   271  	offset := reflect.Indirect(segValue).FieldByName("Offset")
   272  
   273  	// Only the linkedit segment moved, any thing before that is fine.
   274  	if offset.Uint() < linkseg.Offset {
   275  		return nil
   276  	}
   277  	offset.SetUint(offset.Uint() + uint64(linkoffset))
   278  	if err := r.WriteAt(0, seg); err != nil {
   279  		return err
   280  	}
   281  	// There shouldn't be any sections, but just to make sure...
   282  	return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
   283  }
   284  
   285  func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
   286  	iseg := reflect.Indirect(seg)
   287  	nsect := iseg.FieldByName("Nsect").Uint()
   288  	if nsect == 0 {
   289  		return nil
   290  	}
   291  	sectOffset := int64(iseg.Type().Size())
   292  
   293  	isect := reflect.Indirect(sect)
   294  	offsetField := isect.FieldByName("Offset")
   295  	reloffField := isect.FieldByName("Reloff")
   296  	sectSize := int64(isect.Type().Size())
   297  	for i := uint64(0); i < nsect; i++ {
   298  		if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
   299  			return err
   300  		}
   301  		if offsetField.Uint() != 0 {
   302  			offsetField.SetUint(offsetField.Uint() + delta)
   303  		}
   304  		if reloffField.Uint() != 0 {
   305  			reloffField.SetUint(reloffField.Uint() + delta)
   306  		}
   307  		if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
   308  			return err
   309  		}
   310  		sectOffset += sectSize
   311  	}
   312  	return nil
   313  }
   314  
   315  // machoUpdateDwarfHeader updates the DWARF segment load command.
   316  func machoUpdateDwarfHeader(r *loadCmdReader) error {
   317  	var seg, sect interface{}
   318  	cmd, err := r.Next()
   319  	if err != nil {
   320  		return err
   321  	}
   322  	if cmd.Cmd == macho.LoadCmdSegment64 {
   323  		seg = new(macho.Segment64)
   324  		sect = new(macho.Section64)
   325  	} else {
   326  		seg = new(macho.Segment32)
   327  		sect = new(macho.Section32)
   328  	}
   329  	if err := r.ReadAt(0, seg); err != nil {
   330  		return err
   331  	}
   332  	segValue := reflect.ValueOf(seg)
   333  	offset := reflect.Indirect(segValue).FieldByName("Offset")
   334  
   335  	delta := uint64(dwarfstart) - realdwarf.Offset
   336  	offset.SetUint(offset.Uint() + delta)
   337  	if err := r.WriteAt(0, seg); err != nil {
   338  		return err
   339  	}
   340  	return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
   341  }
   342  
   343  func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
   344  	if err := r.ReadAt(0, cmd); err != nil {
   345  		return err
   346  	}
   347  	value := reflect.Indirect(reflect.ValueOf(cmd))
   348  
   349  	for _, name := range fields {
   350  		field := value.FieldByName(name)
   351  		fieldval := field.Uint()
   352  		if fieldval >= linkseg.Offset {
   353  			field.SetUint(fieldval + uint64(linkoffset))
   354  		}
   355  	}
   356  	if err := r.WriteAt(0, cmd); err != nil {
   357  		return err
   358  	}
   359  	return nil
   360  }
   361  
   362  func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
   363  	align := uint64(1 << alignExp)
   364  	if (origAddr % align) == (newAddr % align) {
   365  		return int64(newAddr)
   366  	}
   367  	padding := (align - (newAddr % align))
   368  	padding += origAddr % align
   369  	return int64(padding + newAddr)
   370  }