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 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 }
    369