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 }