github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/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 "compress/zlib" 10 "debug/macho" 11 "encoding/binary" 12 "fmt" 13 "io" 14 "os" 15 "reflect" 16 "unsafe" 17 ) 18 19 var realdwarf, linkseg *macho.Segment 20 var dwarfstart, linkstart int64 21 var dwarfaddr int64 22 var linkoffset uint32 23 24 const ( 25 pageAlign = 12 // 4096 = 1 << 12 26 ) 27 28 type loadCmd struct { 29 Cmd macho.LoadCmd 30 Len uint32 31 } 32 33 type dyldInfoCmd struct { 34 Cmd macho.LoadCmd 35 Len uint32 36 RebaseOff, RebaseLen uint32 37 BindOff, BindLen uint32 38 WeakBindOff, WeakBindLen uint32 39 LazyBindOff, LazyBindLen uint32 40 ExportOff, ExportLen uint32 41 } 42 43 type linkEditDataCmd struct { 44 Cmd macho.LoadCmd 45 Len uint32 46 DataOff, DataLen uint32 47 } 48 49 type encryptionInfoCmd struct { 50 Cmd macho.LoadCmd 51 Len uint32 52 CryptOff, CryptLen uint32 53 CryptId uint32 54 } 55 56 type loadCmdReader struct { 57 offset, next int64 58 f *os.File 59 order binary.ByteOrder 60 } 61 62 func (r *loadCmdReader) Next() (cmd loadCmd, err error) { 63 r.offset = r.next 64 if _, err = r.f.Seek(r.offset, 0); err != nil { 65 return 66 } 67 if err = binary.Read(r.f, r.order, &cmd); err != nil { 68 return 69 } 70 r.next = r.offset + int64(cmd.Len) 71 return 72 } 73 74 func (r loadCmdReader) ReadAt(offset int64, data interface{}) error { 75 if _, err := r.f.Seek(r.offset+offset, 0); err != nil { 76 return err 77 } 78 return binary.Read(r.f, r.order, data) 79 } 80 81 func (r loadCmdReader) WriteAt(offset int64, data interface{}) error { 82 if _, err := r.f.Seek(r.offset+offset, 0); err != nil { 83 return err 84 } 85 return binary.Write(r.f, r.order, data) 86 } 87 88 // machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable. 89 // machoCombineDwarf returns true and skips merging if the input executable is for iOS. 90 // 91 // With internal linking, DWARF is embedded into the executable, this lets us do the 92 // same for external linking. 93 // inexe is the path to the executable with no DWARF. It must have enough room in the macho 94 // header to add the DWARF sections. (Use ld's -headerpad option) 95 // dsym is the path to the macho file containing DWARF from dsymutil. 96 // outexe is the path where the combined executable should be saved. 97 func machoCombineDwarf(ctxt *Link, inexe, dsym, outexe string) (bool, error) { 98 exef, err := os.Open(inexe) 99 if err != nil { 100 return false, err 101 } 102 exem, err := macho.NewFile(exef) 103 if err != nil { 104 return false, err 105 } 106 cmdOffset := unsafe.Sizeof(exem.FileHeader) 107 is64bit := exem.Magic == macho.Magic64 108 if is64bit { 109 // mach_header_64 has one extra uint32. 110 cmdOffset += unsafe.Sizeof(exem.Magic) 111 } 112 // Check for LC_VERSION_MIN_IPHONEOS. 113 reader := loadCmdReader{next: int64(cmdOffset), f: exef, order: exem.ByteOrder} 114 for i := uint32(0); i < exem.Ncmd; i++ { 115 cmd, err := reader.Next() 116 if err != nil { 117 return false, err 118 } 119 if cmd.Cmd == LC_VERSION_MIN_IPHONEOS { 120 // The executable is for iOS, which doesn't support unmapped 121 // segments such as our __DWARF segment. Skip combining. 122 return true, nil 123 } 124 } 125 dwarff, err := os.Open(dsym) 126 if err != nil { 127 return false, err 128 } 129 outf, err := os.Create(outexe) 130 if err != nil { 131 return false, err 132 } 133 outf.Chmod(0755) 134 135 dwarfm, err := macho.NewFile(dwarff) 136 if err != nil { 137 return false, err 138 } 139 140 // The string table needs to be the last thing in the file 141 // for code signing to work. So we'll need to move the 142 // linkedit section, but all the others can be copied directly. 143 linkseg = exem.Segment("__LINKEDIT") 144 if linkseg == nil { 145 return false, fmt.Errorf("missing __LINKEDIT segment") 146 } 147 148 if _, err = exef.Seek(0, 0); err != nil { 149 return false, err 150 } 151 if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil { 152 return false, err 153 } 154 155 realdwarf = dwarfm.Segment("__DWARF") 156 if realdwarf == nil { 157 return false, fmt.Errorf("missing __DWARF segment") 158 } 159 160 // Try to compress the DWARF sections. This includes some Apple 161 // proprietary sections like __apple_types. 162 compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm) 163 if err != nil { 164 return false, err 165 } 166 167 // Now copy the dwarf data into the output. 168 // Kernel requires all loaded segments to be page-aligned in the file, 169 // even though we mark this one as being 0 bytes of virtual address space. 170 dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign) 171 if _, err = outf.Seek(dwarfstart, 0); err != nil { 172 return false, err 173 } 174 dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1)) 175 176 if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil { 177 return false, err 178 } 179 180 // Write out the compressed sections, or the originals if we gave up 181 // on compressing them. 182 var dwarfsize uint64 183 if compressedBytes != nil { 184 dwarfsize = uint64(len(compressedBytes)) 185 if _, err := outf.Write(compressedBytes); err != nil { 186 return false, err 187 } 188 } else { 189 if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil { 190 return false, err 191 } 192 dwarfsize = realdwarf.Filesz 193 } 194 195 // And finally the linkedit section. 196 if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil { 197 return false, err 198 } 199 linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+dwarfsize, pageAlign) 200 linkoffset = uint32(linkstart - int64(linkseg.Offset)) 201 if _, err = outf.Seek(linkstart, 0); err != nil { 202 return false, err 203 } 204 if _, err := io.Copy(outf, exef); err != nil { 205 return false, err 206 } 207 208 // Now we need to update the headers. 209 textsect := exem.Section("__text") 210 if linkseg == nil { 211 return false, fmt.Errorf("missing __text section") 212 } 213 214 dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz) 215 availablePadding := int64(textsect.Offset) - dwarfCmdOffset 216 if availablePadding < int64(realdwarf.Len) { 217 return false, fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding) 218 } 219 // First, copy the dwarf load command into the header. It will be 220 // updated later with new offsets and lengths as necessary. 221 if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil { 222 return false, err 223 } 224 if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil { 225 return false, err 226 } 227 if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil { 228 return false, err 229 } 230 if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil { 231 return false, err 232 } 233 if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil { 234 return false, err 235 } 236 237 reader = loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} 238 for i := uint32(0); i < exem.Ncmd; i++ { 239 cmd, err := reader.Next() 240 if err != nil { 241 return false, err 242 } 243 switch cmd.Cmd { 244 case macho.LoadCmdSegment64: 245 err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{}) 246 case macho.LoadCmdSegment: 247 err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{}) 248 case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: 249 err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") 250 case macho.LoadCmdSymtab: 251 err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff") 252 case macho.LoadCmdDysymtab: 253 err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") 254 case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS: 255 err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff") 256 case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: 257 err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff") 258 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: 259 // Nothing to update 260 default: 261 err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd) 262 } 263 if err != nil { 264 return false, err 265 } 266 } 267 // Do the final update of the DWARF segment's load command. 268 return false, machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize) 269 } 270 271 // machoCompressSections tries to compress the DWARF segments in dwarfm, 272 // returning the updated sections and segment contents, nils if the sections 273 // weren't compressed, or an error if there was a problem reading dwarfm. 274 func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) { 275 if !ctxt.compressDWARF { 276 return nil, nil, nil 277 } 278 279 dwarfseg := dwarfm.Segment("__DWARF") 280 var sects []*macho.Section 281 var bytes []byte 282 283 for _, sect := range dwarfm.Sections { 284 if sect.Seg != "__DWARF" { 285 continue 286 } 287 288 // As of writing, there are no relocations in dsymutil's output 289 // so there's no point in worrying about them. Bail out if that 290 // changes. 291 if sect.Nreloc != 0 { 292 return nil, nil, nil 293 } 294 295 data, err := sect.Data() 296 if err != nil { 297 return nil, nil, err 298 } 299 300 compressed, contents, err := machoCompressSection(data) 301 if err != nil { 302 return nil, nil, err 303 } 304 305 newSec := *sect 306 newSec.Offset = uint32(dwarfseg.Offset) + uint32(len(bytes)) 307 newSec.Addr = dwarfseg.Addr + uint64(len(bytes)) 308 if compressed { 309 newSec.Name = "__z" + sect.Name[2:] 310 newSec.Size = uint64(len(contents)) 311 } 312 sects = append(sects, &newSec) 313 bytes = append(bytes, contents...) 314 } 315 return sects, bytes, nil 316 } 317 318 // machoCompressSection compresses secBytes if it results in less data. 319 func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) { 320 var buf bytes.Buffer 321 buf.Write([]byte("ZLIB")) 322 var sizeBytes [8]byte 323 binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes))) 324 buf.Write(sizeBytes[:]) 325 326 z := zlib.NewWriter(&buf) 327 if _, err := z.Write(sectBytes); err != nil { 328 return false, nil, err 329 } 330 if err := z.Close(); err != nil { 331 return false, nil, err 332 } 333 if len(buf.Bytes()) >= len(sectBytes) { 334 return false, sectBytes, nil 335 } 336 return true, buf.Bytes(), nil 337 } 338 339 // machoUpdateSegment updates the load command for a moved segment. 340 // Only the linkedit segment should move, and it should have 0 sections. 341 // seg should be a macho.Segment32 or macho.Segment64 as appropriate. 342 // sect should be a macho.Section32 or macho.Section64 as appropriate. 343 func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error { 344 if err := r.ReadAt(0, seg); err != nil { 345 return err 346 } 347 segValue := reflect.ValueOf(seg) 348 offset := reflect.Indirect(segValue).FieldByName("Offset") 349 350 // Only the linkedit segment moved, any thing before that is fine. 351 if offset.Uint() < linkseg.Offset { 352 return nil 353 } 354 offset.SetUint(offset.Uint() + uint64(linkoffset)) 355 if err := r.WriteAt(0, seg); err != nil { 356 return err 357 } 358 // There shouldn't be any sections, but just to make sure... 359 return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0, nil) 360 } 361 362 func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64, compressedSects []*macho.Section) error { 363 iseg := reflect.Indirect(seg) 364 nsect := iseg.FieldByName("Nsect").Uint() 365 if nsect == 0 { 366 return nil 367 } 368 sectOffset := int64(iseg.Type().Size()) 369 370 isect := reflect.Indirect(sect) 371 offsetField := isect.FieldByName("Offset") 372 reloffField := isect.FieldByName("Reloff") 373 addrField := isect.FieldByName("Addr") 374 nameField := isect.FieldByName("Name") 375 sizeField := isect.FieldByName("Size") 376 sectSize := int64(isect.Type().Size()) 377 for i := uint64(0); i < nsect; i++ { 378 if err := r.ReadAt(sectOffset, sect.Interface()); err != nil { 379 return err 380 } 381 if compressedSects != nil { 382 cSect := compressedSects[i] 383 var name [16]byte 384 copy(name[:], []byte(cSect.Name)) 385 nameField.Set(reflect.ValueOf(name)) 386 sizeField.SetUint(cSect.Size) 387 if cSect.Offset != 0 { 388 offsetField.SetUint(uint64(cSect.Offset) + deltaOffset) 389 } 390 if cSect.Addr != 0 { 391 addrField.SetUint(cSect.Addr + deltaAddr) 392 } 393 } else { 394 if offsetField.Uint() != 0 { 395 offsetField.SetUint(offsetField.Uint() + deltaOffset) 396 } 397 if reloffField.Uint() != 0 { 398 reloffField.SetUint(reloffField.Uint() + deltaOffset) 399 } 400 if addrField.Uint() != 0 { 401 addrField.SetUint(addrField.Uint() + deltaAddr) 402 } 403 } 404 if err := r.WriteAt(sectOffset, sect.Interface()); err != nil { 405 return err 406 } 407 sectOffset += sectSize 408 } 409 return nil 410 } 411 412 // machoUpdateDwarfHeader updates the DWARF segment load command. 413 func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64) error { 414 var seg, sect interface{} 415 cmd, err := r.Next() 416 if err != nil { 417 return err 418 } 419 if cmd.Cmd == macho.LoadCmdSegment64 { 420 seg = new(macho.Segment64) 421 sect = new(macho.Section64) 422 } else { 423 seg = new(macho.Segment32) 424 sect = new(macho.Section32) 425 } 426 if err := r.ReadAt(0, seg); err != nil { 427 return err 428 } 429 segv := reflect.ValueOf(seg).Elem() 430 segv.FieldByName("Offset").SetUint(uint64(dwarfstart)) 431 432 if compressedSects != nil { 433 var segSize uint64 434 for _, newSect := range compressedSects { 435 segSize += newSect.Size 436 } 437 segv.FieldByName("Filesz").SetUint(segSize) 438 } else { 439 segv.FieldByName("Filesz").SetUint(dwarfsize) 440 } 441 442 deltaOffset := uint64(dwarfstart) - realdwarf.Offset 443 deltaAddr := uint64(dwarfaddr) - realdwarf.Addr 444 445 // We want the DWARF segment to be considered non-loadable, so 446 // force vmaddr and vmsize to zero. In addition, set the initial 447 // protection to zero so as to make the dynamic loader happy, 448 // since otherwise it may complain that that the vm size and file 449 // size don't match for the segment. See issues 21647 and 32673 450 // for more context. Also useful to refer to the Apple dynamic 451 // loader source, specifically ImageLoaderMachO::sniffLoadCommands 452 // in ImageLoaderMachO.cpp (various versions can be found online, see 453 // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html 454 // as one example). 455 segv.FieldByName("Addr").SetUint(0) 456 segv.FieldByName("Memsz").SetUint(0) 457 segv.FieldByName("Prot").SetUint(0) 458 deltaAddr = 0 459 460 if err := r.WriteAt(0, seg); err != nil { 461 return err 462 } 463 return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr, compressedSects) 464 } 465 466 func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error { 467 if err := r.ReadAt(0, cmd); err != nil { 468 return err 469 } 470 value := reflect.Indirect(reflect.ValueOf(cmd)) 471 472 for _, name := range fields { 473 field := value.FieldByName(name) 474 fieldval := field.Uint() 475 if fieldval >= linkseg.Offset { 476 field.SetUint(fieldval + uint64(linkoffset)) 477 } 478 } 479 if err := r.WriteAt(0, cmd); err != nil { 480 return err 481 } 482 return nil 483 } 484 485 func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 { 486 align := uint64(1 << alignExp) 487 if (origAddr % align) == (newAddr % align) { 488 return int64(newAddr) 489 } 490 padding := (align - (newAddr % align)) 491 padding += origAddr % align 492 return int64(padding + newAddr) 493 }