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