github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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, linkaddr int64 21 var linkoffset uint32 22 23 const ( 24 LC_ID_DYLIB = 0xd 25 LC_LOAD_DYLINKER = 0xe 26 LC_PREBOUND_DYLIB = 0x10 27 LC_LOAD_WEAK_DYLIB = 0x18 28 LC_UUID = 0x1b 29 LC_RPATH = 0x8000001c 30 LC_CODE_SIGNATURE = 0x1d 31 LC_SEGMENT_SPLIT_INFO = 0x1e 32 LC_REEXPORT_DYLIB = 0x8000001f 33 LC_ENCRYPTION_INFO = 0x21 34 LC_DYLD_INFO = 0x22 35 LC_DYLD_INFO_ONLY = 0x80000022 36 LC_VERSION_MIN_MACOSX = 0x24 37 LC_VERSION_MIN_IPHONEOS = 0x25 38 LC_FUNCTION_STARTS = 0x26 39 LC_MAIN = 0x80000028 40 LC_DATA_IN_CODE = 0x29 41 LC_SOURCE_VERSION = 0x2A 42 LC_DYLIB_CODE_SIGN_DRS = 0x2B 43 LC_ENCRYPTION_INFO_64 = 0x2C 44 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 // Kernel requires all loaded segments to be page-aligned in the file, 161 // even though we mark this one as being 0 bytes of virtual address space. 162 dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign) 163 if _, err = outf.Seek(dwarfstart, 0); err != nil { 164 return err 165 } 166 dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1)) 167 168 if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil { 169 return err 170 } 171 if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil { 172 return err 173 } 174 175 // And finally the linkedit section. 176 if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil { 177 return err 178 } 179 linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign) 180 linkoffset = uint32(linkstart - int64(linkseg.Offset)) 181 if _, err = outf.Seek(linkstart, 0); err != nil { 182 return err 183 } 184 if _, err := io.Copy(outf, exef); err != nil { 185 return err 186 } 187 188 // Now we need to update the headers. 189 cmdOffset := unsafe.Sizeof(exem.FileHeader) 190 is64bit := exem.Magic == macho.Magic64 191 if is64bit { 192 // mach_header_64 has one extra uint32. 193 cmdOffset += unsafe.Sizeof(exem.Magic) 194 } 195 196 textsect := exem.Section("__text") 197 if linkseg == nil { 198 return fmt.Errorf("missing __text section") 199 } 200 201 dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz) 202 availablePadding := int64(textsect.Offset) - dwarfCmdOffset 203 if availablePadding < int64(realdwarf.Len) { 204 return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding) 205 } 206 // First, copy the dwarf load command into the header 207 if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil { 208 return err 209 } 210 if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil { 211 return err 212 } 213 214 if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil { 215 return err 216 } 217 if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil { 218 return err 219 } 220 if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil { 221 return err 222 } 223 224 reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} 225 for i := uint32(0); i < exem.Ncmd; i++ { 226 cmd, err := reader.Next() 227 if err != nil { 228 return err 229 } 230 switch cmd.Cmd { 231 case macho.LoadCmdSegment64: 232 err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{}) 233 case macho.LoadCmdSegment: 234 err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{}) 235 case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: 236 err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") 237 case macho.LoadCmdSymtab: 238 err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff") 239 case macho.LoadCmdDysymtab: 240 err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") 241 case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS: 242 err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff") 243 case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: 244 err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff") 245 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: 246 // Nothing to update 247 default: 248 err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd) 249 } 250 if err != nil { 251 return err 252 } 253 } 254 return machoUpdateDwarfHeader(&reader) 255 } 256 257 // machoUpdateSegment updates the load command for a moved segment. 258 // Only the linkedit segment should move, and it should have 0 sections. 259 // seg should be a macho.Segment32 or macho.Segment64 as appropriate. 260 // sect should be a macho.Section32 or macho.Section64 as appropriate. 261 func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error { 262 if err := r.ReadAt(0, seg); err != nil { 263 return err 264 } 265 segValue := reflect.ValueOf(seg) 266 offset := reflect.Indirect(segValue).FieldByName("Offset") 267 268 // Only the linkedit segment moved, any thing before that is fine. 269 if offset.Uint() < linkseg.Offset { 270 return nil 271 } 272 offset.SetUint(offset.Uint() + uint64(linkoffset)) 273 if err := r.WriteAt(0, seg); err != nil { 274 return err 275 } 276 // There shouldn't be any sections, but just to make sure... 277 return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0) 278 } 279 280 func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64) error { 281 iseg := reflect.Indirect(seg) 282 nsect := iseg.FieldByName("Nsect").Uint() 283 if nsect == 0 { 284 return nil 285 } 286 sectOffset := int64(iseg.Type().Size()) 287 288 isect := reflect.Indirect(sect) 289 offsetField := isect.FieldByName("Offset") 290 reloffField := isect.FieldByName("Reloff") 291 addrField := isect.FieldByName("Addr") 292 sectSize := int64(isect.Type().Size()) 293 for i := uint64(0); i < nsect; i++ { 294 if err := r.ReadAt(sectOffset, sect.Interface()); err != nil { 295 return err 296 } 297 if offsetField.Uint() != 0 { 298 offsetField.SetUint(offsetField.Uint() + deltaOffset) 299 } 300 if reloffField.Uint() != 0 { 301 reloffField.SetUint(reloffField.Uint() + deltaOffset) 302 } 303 if addrField.Uint() != 0 { 304 addrField.SetUint(addrField.Uint() + deltaAddr) 305 } 306 if err := r.WriteAt(sectOffset, sect.Interface()); err != nil { 307 return err 308 } 309 sectOffset += sectSize 310 } 311 return nil 312 } 313 314 // machoUpdateDwarfHeader updates the DWARF segment load command. 315 func machoUpdateDwarfHeader(r *loadCmdReader) error { 316 var seg, sect interface{} 317 cmd, err := r.Next() 318 if err != nil { 319 return err 320 } 321 if cmd.Cmd == macho.LoadCmdSegment64 { 322 seg = new(macho.Segment64) 323 sect = new(macho.Section64) 324 } else { 325 seg = new(macho.Segment32) 326 sect = new(macho.Section32) 327 } 328 if err := r.ReadAt(0, seg); err != nil { 329 return err 330 } 331 segv := reflect.ValueOf(seg).Elem() 332 333 segv.FieldByName("Offset").SetUint(uint64(dwarfstart)) 334 segv.FieldByName("Addr").SetUint(uint64(dwarfaddr)) 335 336 deltaOffset := uint64(dwarfstart) - realdwarf.Offset 337 deltaAddr := uint64(dwarfaddr) - realdwarf.Addr 338 339 // If we set Memsz to 0 (and might as well set Addr too), 340 // then the xnu kernel will bail out halfway through load_segment 341 // and not apply further sanity checks that we might fail in the future. 342 // We don't need the DWARF information actually available in memory. 343 // But if we do this for buildmode=c-shared then the user-space 344 // dynamic loader complains about memsz < filesz. Sigh. 345 if Buildmode != BuildmodeCShared { 346 segv.FieldByName("Addr").SetUint(0) 347 segv.FieldByName("Memsz").SetUint(0) 348 deltaAddr = 0 349 } 350 351 if err := r.WriteAt(0, seg); err != nil { 352 return err 353 } 354 return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr) 355 } 356 357 func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error { 358 if err := r.ReadAt(0, cmd); err != nil { 359 return err 360 } 361 value := reflect.Indirect(reflect.ValueOf(cmd)) 362 363 for _, name := range fields { 364 field := value.FieldByName(name) 365 fieldval := field.Uint() 366 if fieldval >= linkseg.Offset { 367 field.SetUint(fieldval + uint64(linkoffset)) 368 } 369 } 370 if err := r.WriteAt(0, cmd); err != nil { 371 return err 372 } 373 return nil 374 } 375 376 func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 { 377 align := uint64(1 << alignExp) 378 if (origAddr % align) == (newAddr % align) { 379 return int64(newAddr) 380 } 381 padding := (align - (newAddr % align)) 382 padding += origAddr % align 383 return int64(padding + newAddr) 384 }