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