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