github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/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 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 }