Home | History | Annotate | Download | only in ld
      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 }
    371