diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 89a1c0ec8b4439..7b2b96fe002c73 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -46,6 +46,7 @@ var bootstrapDirs = []string{ "cmd/internal/gcprog", "cmd/internal/goobj", "cmd/internal/hash", + "cmd/internal/macho", "cmd/internal/obj/...", "cmd/internal/objabi", "cmd/internal/pgo", diff --git a/src/cmd/internal/macho/macho.go b/src/cmd/internal/macho/macho.go new file mode 100644 index 00000000000000..ad29c32c50a8de --- /dev/null +++ b/src/cmd/internal/macho/macho.go @@ -0,0 +1,134 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package macho provides functionalities to handle Mach-O +// beyond the debug/macho package, for the toolchain. +package macho + +import ( + "debug/macho" + "encoding/binary" + "io" + "unsafe" +) + +const ( + LC_SEGMENT = 0x1 + LC_SYMTAB = 0x2 + LC_SYMSEG = 0x3 + LC_THREAD = 0x4 + LC_UNIXTHREAD = 0x5 + LC_LOADFVMLIB = 0x6 + LC_IDFVMLIB = 0x7 + LC_IDENT = 0x8 + LC_FVMFILE = 0x9 + LC_PREPAGE = 0xa + LC_DYSYMTAB = 0xb + LC_LOAD_DYLIB = 0xc + LC_ID_DYLIB = 0xd + LC_LOAD_DYLINKER = 0xe + LC_ID_DYLINKER = 0xf + LC_PREBOUND_DYLIB = 0x10 + LC_ROUTINES = 0x11 + LC_SUB_FRAMEWORK = 0x12 + LC_SUB_UMBRELLA = 0x13 + LC_SUB_CLIENT = 0x14 + LC_SUB_LIBRARY = 0x15 + LC_TWOLEVEL_HINTS = 0x16 + LC_PREBIND_CKSUM = 0x17 + LC_LOAD_WEAK_DYLIB = 0x80000018 + LC_SEGMENT_64 = 0x19 + LC_ROUTINES_64 = 0x1a + LC_UUID = 0x1b + LC_RPATH = 0x8000001c + LC_CODE_SIGNATURE = 0x1d + LC_SEGMENT_SPLIT_INFO = 0x1e + LC_REEXPORT_DYLIB = 0x8000001f + LC_LAZY_LOAD_DYLIB = 0x20 + LC_ENCRYPTION_INFO = 0x21 + LC_DYLD_INFO = 0x22 + LC_DYLD_INFO_ONLY = 0x80000022 + LC_LOAD_UPWARD_DYLIB = 0x80000023 + LC_VERSION_MIN_MACOSX = 0x24 + LC_VERSION_MIN_IPHONEOS = 0x25 + LC_FUNCTION_STARTS = 0x26 + LC_DYLD_ENVIRONMENT = 0x27 + LC_MAIN = 0x80000028 + LC_DATA_IN_CODE = 0x29 + LC_SOURCE_VERSION = 0x2A + LC_DYLIB_CODE_SIGN_DRS = 0x2B + LC_ENCRYPTION_INFO_64 = 0x2C + LC_LINKER_OPTION = 0x2D + LC_LINKER_OPTIMIZATION_HINT = 0x2E + LC_VERSION_MIN_TVOS = 0x2F + LC_VERSION_MIN_WATCHOS = 0x30 + LC_VERSION_NOTE = 0x31 + LC_BUILD_VERSION = 0x32 + LC_DYLD_EXPORTS_TRIE = 0x80000033 + LC_DYLD_CHAINED_FIXUPS = 0x80000034 +) + +// LoadCmd is macho.LoadCmd with its length, which is also +// the load command header in the Mach-O file. +type LoadCmd struct { + Cmd macho.LoadCmd + Len uint32 +} + +type LoadCmdReader struct { + offset, next int64 + f io.ReadSeeker + order binary.ByteOrder +} + +func NewLoadCmdReader(f io.ReadSeeker, order binary.ByteOrder, nextOffset int64) LoadCmdReader { + return LoadCmdReader{next: nextOffset, f: f, order: order} +} + +func (r *LoadCmdReader) Next() (LoadCmd, error) { + var cmd LoadCmd + + r.offset = r.next + if _, err := r.f.Seek(r.offset, 0); err != nil { + return cmd, err + } + if err := binary.Read(r.f, r.order, &cmd); err != nil { + return cmd, err + } + r.next = r.offset + int64(cmd.Len) + return cmd, nil +} + +func (r LoadCmdReader) ReadAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Read(r.f, r.order, data) +} + +func (r LoadCmdReader) Offset() int64 { return r.offset } + +type LoadCmdUpdater struct { + LoadCmdReader +} + +func NewLoadCmdUpdater(f io.ReadWriteSeeker, order binary.ByteOrder, nextOffset int64) LoadCmdUpdater { + return LoadCmdUpdater{NewLoadCmdReader(f, order, nextOffset)} +} + +func (u LoadCmdUpdater) WriteAt(offset int64, data interface{}) error { + if _, err := u.f.Seek(u.offset+offset, 0); err != nil { + return err + } + return binary.Write(u.f.(io.Writer), u.order, data) +} + +func FileHeaderSize(f *macho.File) int64 { + offset := int64(unsafe.Sizeof(f.FileHeader)) + if is64bit := f.Magic == macho.Magic64; is64bit { + // mach_header_64 has one extra uint32. + offset += 4 + } + return offset +} diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index c5a85f0e75e7cf..eb2bd6bff6279e 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -7,6 +7,7 @@ package ld import ( "bytes" "cmd/internal/codesign" + imacho "cmd/internal/macho" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" @@ -127,62 +128,6 @@ const ( MH_PIE = 0x200000 ) -const ( - LC_SEGMENT = 0x1 - LC_SYMTAB = 0x2 - LC_SYMSEG = 0x3 - LC_THREAD = 0x4 - LC_UNIXTHREAD = 0x5 - LC_LOADFVMLIB = 0x6 - LC_IDFVMLIB = 0x7 - LC_IDENT = 0x8 - LC_FVMFILE = 0x9 - LC_PREPAGE = 0xa - LC_DYSYMTAB = 0xb - LC_LOAD_DYLIB = 0xc - LC_ID_DYLIB = 0xd - LC_LOAD_DYLINKER = 0xe - LC_ID_DYLINKER = 0xf - LC_PREBOUND_DYLIB = 0x10 - LC_ROUTINES = 0x11 - LC_SUB_FRAMEWORK = 0x12 - LC_SUB_UMBRELLA = 0x13 - LC_SUB_CLIENT = 0x14 - LC_SUB_LIBRARY = 0x15 - LC_TWOLEVEL_HINTS = 0x16 - LC_PREBIND_CKSUM = 0x17 - LC_LOAD_WEAK_DYLIB = 0x80000018 - LC_SEGMENT_64 = 0x19 - LC_ROUTINES_64 = 0x1a - LC_UUID = 0x1b - LC_RPATH = 0x8000001c - LC_CODE_SIGNATURE = 0x1d - LC_SEGMENT_SPLIT_INFO = 0x1e - LC_REEXPORT_DYLIB = 0x8000001f - LC_LAZY_LOAD_DYLIB = 0x20 - LC_ENCRYPTION_INFO = 0x21 - LC_DYLD_INFO = 0x22 - LC_DYLD_INFO_ONLY = 0x80000022 - LC_LOAD_UPWARD_DYLIB = 0x80000023 - LC_VERSION_MIN_MACOSX = 0x24 - LC_VERSION_MIN_IPHONEOS = 0x25 - LC_FUNCTION_STARTS = 0x26 - LC_DYLD_ENVIRONMENT = 0x27 - LC_MAIN = 0x80000028 - LC_DATA_IN_CODE = 0x29 - LC_SOURCE_VERSION = 0x2A - LC_DYLIB_CODE_SIGN_DRS = 0x2B - LC_ENCRYPTION_INFO_64 = 0x2C - LC_LINKER_OPTION = 0x2D - LC_LINKER_OPTIMIZATION_HINT = 0x2E - LC_VERSION_MIN_TVOS = 0x2F - LC_VERSION_MIN_WATCHOS = 0x30 - LC_VERSION_NOTE = 0x31 - LC_BUILD_VERSION = 0x32 - LC_DYLD_EXPORTS_TRIE = 0x80000033 - LC_DYLD_CHAINED_FIXUPS = 0x80000034 -) - const ( S_REGULAR = 0x0 S_ZEROFILL = 0x1 @@ -387,7 +332,7 @@ func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int for i := 0; i < nseg; i++ { s := &seg[i] if arch.PtrSize == 8 { - out.Write32(LC_SEGMENT_64) + out.Write32(imacho.LC_SEGMENT_64) out.Write32(72 + 80*s.nsect) out.WriteStringN(s.name, 16) out.Write64(s.vaddr) @@ -399,7 +344,7 @@ func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int out.Write32(s.nsect) out.Write32(s.flag) } else { - out.Write32(LC_SEGMENT) + out.Write32(imacho.LC_SEGMENT) out.Write32(56 + 68*s.nsect) out.WriteStringN(s.name, 16) out.Write32(uint32(s.vaddr)) @@ -488,7 +433,7 @@ func (ctxt *Link) domacho() { // In general this can be the most recent supported macOS version. version = 11<<16 | 0<<8 | 0<<0 // 11.0.0 } - ml := newMachoLoad(ctxt.Arch, LC_BUILD_VERSION, 4) + ml := newMachoLoad(ctxt.Arch, imacho.LC_BUILD_VERSION, 4) ml.data[0] = uint32(machoPlatform) ml.data[1] = version // OS version ml.data[2] = version // SDK version @@ -778,14 +723,14 @@ func asmbMacho(ctxt *Link) { Exitf("unknown macho architecture: %v", ctxt.Arch.Family) case sys.AMD64: - ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2) + ml := newMachoLoad(ctxt.Arch, imacho.LC_UNIXTHREAD, 42+2) ml.data[0] = 4 /* thread type */ ml.data[1] = 42 /* word count */ ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */ ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) case sys.ARM64: - ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4) + ml := newMachoLoad(ctxt.Arch, imacho.LC_MAIN, 4) ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32) } @@ -815,7 +760,7 @@ func asmbMacho(ctxt *Link) { } if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() { - ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10) + ml := newMachoLoad(ctxt.Arch, imacho.LC_DYLD_INFO_ONLY, 10) ml.data[0] = uint32(linkoff) // rebase off ml.data[1] = uint32(s1) // rebase size ml.data[2] = uint32(linkoff + s1) // bind off @@ -828,7 +773,7 @@ func asmbMacho(ctxt *Link) { ml.data[9] = 0 // export size } - ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) + ml := newMachoLoad(ctxt.Arch, imacho.LC_SYMTAB, 4) ml.data[0] = uint32(linkoff + s1 + s2) /* symoff */ ml.data[1] = uint32(nsortsym) /* nsyms */ ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) /* stroff */ @@ -837,12 +782,12 @@ func asmbMacho(ctxt *Link) { if ctxt.LinkMode != LinkExternal { machodysymtab(ctxt, linkoff+s1+s2) - ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6) + ml := newMachoLoad(ctxt.Arch, imacho.LC_LOAD_DYLINKER, 6) ml.data[0] = 12 /* offset to string */ stringtouint32(ml.data[1:], "/usr/lib/dyld") for _, lib := range dylib { - ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) + ml = newMachoLoad(ctxt.Arch, imacho.LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) ml.data[0] = 24 /* offset of string from beginning of load */ ml.data[1] = 0 /* time stamp */ ml.data[2] = 0 /* version */ @@ -852,7 +797,7 @@ func asmbMacho(ctxt *Link) { } if ctxt.IsInternal() && len(buildinfo) > 0 { - ml := newMachoLoad(ctxt.Arch, LC_UUID, 4) + ml := newMachoLoad(ctxt.Arch, imacho.LC_UUID, 4) // Mach-O UUID is 16 bytes if len(buildinfo) < 16 { buildinfo = append(buildinfo, make([]byte, 16)...) @@ -866,7 +811,7 @@ func asmbMacho(ctxt *Link) { } if ctxt.IsInternal() && ctxt.NeedCodeSign() { - ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2) + ml := newMachoLoad(ctxt.Arch, imacho.LC_CODE_SIGNATURE, 2) ml.data[0] = uint32(codesigOff) ml.data[1] = uint32(s7) } @@ -1126,7 +1071,7 @@ func machosymtab(ctxt *Link) { } func machodysymtab(ctxt *Link, base int64) { - ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18) + ml := newMachoLoad(ctxt.Arch, imacho.LC_DYSYMTAB, 18) n := 0 ml.data[0] = uint32(n) /* ilocalsym */ @@ -1339,15 +1284,15 @@ func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { data := raw[8:] var p MachoPlatform switch ml.type_ { - case LC_VERSION_MIN_IPHONEOS: + case imacho.LC_VERSION_MIN_IPHONEOS: p = PLATFORM_IOS - case LC_VERSION_MIN_MACOSX: + case imacho.LC_VERSION_MIN_MACOSX: p = PLATFORM_MACOS - case LC_VERSION_MIN_WATCHOS: + case imacho.LC_VERSION_MIN_WATCHOS: p = PLATFORM_WATCHOS - case LC_VERSION_MIN_TVOS: + case imacho.LC_VERSION_MIN_TVOS: p = PLATFORM_TVOS - case LC_BUILD_VERSION: + case imacho.LC_BUILD_VERSION: p = MachoPlatform(m.ByteOrder.Uint32(data)) default: continue @@ -1536,7 +1481,7 @@ func machoCodeSign(ctxt *Link, fname string) error { for _, l := range mf.Loads { data := l.Raw() cmd, sz := get32(data), get32(data[4:]) - if cmd == LC_CODE_SIGNATURE { + if cmd == imacho.LC_CODE_SIGNATURE { sigOff = int64(get32(data[8:])) sigSz = int64(get32(data[12:])) csCmdOff = loadOff diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go index 0a41e30eb35dc3..66c405746b359d 100644 --- a/src/cmd/link/internal/ld/macho_combine_dwarf.go +++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go @@ -5,6 +5,8 @@ package ld import ( + imacho "cmd/internal/macho" + "bytes" "compress/zlib" "debug/macho" @@ -16,11 +18,6 @@ import ( "unsafe" ) -type loadCmd struct { - Cmd macho.LoadCmd - Len uint32 -} - type dyldInfoCmd struct { Cmd macho.LoadCmd Len uint32 @@ -50,40 +47,6 @@ type uuidCmd struct { Uuid [16]byte } -type loadCmdReader struct { - offset, next int64 - f *os.File - order binary.ByteOrder -} - -func (r *loadCmdReader) Next() (loadCmd, error) { - var cmd loadCmd - - r.offset = r.next - if _, err := r.f.Seek(r.offset, 0); err != nil { - return cmd, err - } - if err := binary.Read(r.f, r.order, &cmd); err != nil { - return cmd, err - } - r.next = r.offset + int64(cmd.Len) - return cmd, nil -} - -func (r loadCmdReader) ReadAt(offset int64, data interface{}) error { - if _, err := r.f.Seek(r.offset+offset, 0); err != nil { - return err - } - return binary.Read(r.f, r.order, data) -} - -func (r loadCmdReader) WriteAt(offset int64, data interface{}) error { - if _, err := r.f.Seek(r.offset+offset, 0); err != nil { - return err - } - return binary.Write(r.f, r.order, data) -} - // machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable. // // With internal linking, DWARF is embedded into the executable, this lets us do the @@ -182,11 +145,7 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe return fmt.Errorf("missing __text section") } - cmdOffset := unsafe.Sizeof(exem.FileHeader) - if is64bit := exem.Magic == macho.Magic64; is64bit { - // mach_header_64 has one extra uint32. - cmdOffset += unsafe.Sizeof(exem.Magic) - } + cmdOffset := imacho.FileHeaderSize(exem) dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz availablePadding := textsect.Offset - dwarfCmdOffset if availablePadding < realdwarf.Len { @@ -210,7 +169,7 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe return err } - reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} + reader := imacho.NewLoadCmdUpdater(outf, exem.ByteOrder, cmdOffset) for i := uint32(0); i < exem.Ncmd; i++ { cmd, err := reader.Next() if err != nil { @@ -222,18 +181,18 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe err = machoUpdateSegment(reader, linkseg, linkoffset) case macho.LoadCmdSegment: panic("unexpected 32-bit segment") - case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: + case imacho.LC_DYLD_INFO, imacho.LC_DYLD_INFO_ONLY: err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") case macho.LoadCmdSymtab: err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff") case macho.LoadCmdDysymtab: err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") - case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, - LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS: + case imacho.LC_CODE_SIGNATURE, imacho.LC_SEGMENT_SPLIT_INFO, imacho.LC_FUNCTION_STARTS, imacho.LC_DATA_IN_CODE, imacho.LC_DYLIB_CODE_SIGN_DRS, + imacho.LC_DYLD_EXPORTS_TRIE, imacho.LC_DYLD_CHAINED_FIXUPS: err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff") - case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: + case imacho.LC_ENCRYPTION_INFO, imacho.LC_ENCRYPTION_INFO_64: err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff") - case LC_UUID: + case imacho.LC_UUID: var u uuidCmd err = reader.ReadAt(0, &u) if err == nil { @@ -241,13 +200,13 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe err = reader.WriteAt(0, &u) } case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, - LC_PREBOUND_DYLIB, 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: + imacho.LC_PREBOUND_DYLIB, imacho.LC_VERSION_MIN_MACOSX, imacho.LC_VERSION_MIN_IPHONEOS, imacho.LC_SOURCE_VERSION, + imacho.LC_MAIN, imacho.LC_LOAD_DYLINKER, imacho.LC_LOAD_WEAK_DYLIB, imacho.LC_REEXPORT_DYLIB, imacho.LC_RPATH, imacho.LC_ID_DYLIB, + imacho.LC_SYMSEG, imacho.LC_LOADFVMLIB, imacho.LC_IDFVMLIB, imacho.LC_IDENT, imacho.LC_FVMFILE, imacho.LC_PREPAGE, imacho.LC_ID_DYLINKER, + imacho.LC_ROUTINES, imacho.LC_SUB_FRAMEWORK, imacho.LC_SUB_UMBRELLA, imacho.LC_SUB_CLIENT, imacho.LC_SUB_LIBRARY, imacho.LC_TWOLEVEL_HINTS, + imacho.LC_PREBIND_CKSUM, imacho.LC_ROUTINES_64, imacho.LC_LAZY_LOAD_DYLIB, imacho.LC_LOAD_UPWARD_DYLIB, imacho.LC_DYLD_ENVIRONMENT, + imacho.LC_LINKER_OPTION, imacho.LC_LINKER_OPTIMIZATION_HINT, imacho.LC_VERSION_MIN_TVOS, imacho.LC_VERSION_MIN_WATCHOS, + imacho.LC_VERSION_NOTE, imacho.LC_BUILD_VERSION: // Nothing to update default: err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd) @@ -330,7 +289,7 @@ func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, e // machoUpdateSegment updates the load command for a moved segment. // Only the linkedit segment should move, and it should have 0 sections. -func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64) error { +func machoUpdateSegment(r imacho.LoadCmdUpdater, linkseg *macho.Segment, linkoffset uint64) error { var seg macho.Segment64 if err := r.ReadAt(0, &seg); err != nil { return err @@ -348,7 +307,7 @@ func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint return machoUpdateSections(r, &seg, linkoffset, nil) } -func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error { +func machoUpdateSections(r imacho.LoadCmdUpdater, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error { nsect := seg.Nsect if nsect == 0 { return nil @@ -388,7 +347,7 @@ func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint } // machoUpdateDwarfHeader updates the DWARF segment load command. -func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error { +func machoUpdateDwarfHeader(r *imacho.LoadCmdUpdater, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error { cmd, err := r.Next() if err != nil { return err @@ -432,7 +391,7 @@ func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, return machoUpdateSections(*r, &seg, uint64(dwarfstart)-realdwarf.Offset, compressedSects) } -func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error { +func machoUpdateLoadCommand(r imacho.LoadCmdUpdater, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error { if err := r.ReadAt(0, cmd); err != nil { return err } diff --git a/src/cmd/link/internal/ld/macho_update_uuid.go b/src/cmd/link/internal/ld/macho_update_uuid.go index df34969e4757d8..607cdad996a8dd 100644 --- a/src/cmd/link/internal/ld/macho_update_uuid.go +++ b/src/cmd/link/internal/ld/macho_update_uuid.go @@ -19,10 +19,11 @@ package ld import ( "cmd/internal/hash" + imacho "cmd/internal/macho" + "debug/macho" "io" "os" - "unsafe" ) // uuidFromGoBuildId hashes the Go build ID and returns a slice of 16 @@ -66,26 +67,21 @@ func machoRewriteUuid(ctxt *Link, exef *os.File, exem *macho.File, outexe string } // Locate the portion of the binary containing the load commands. - cmdOffset := unsafe.Sizeof(exem.FileHeader) - if is64bit := exem.Magic == macho.Magic64; is64bit { - // mach_header_64 has one extra uint32. - cmdOffset += unsafe.Sizeof(exem.Magic) - } - if _, err := outf.Seek(int64(cmdOffset), 0); err != nil { + cmdOffset := imacho.FileHeaderSize(exem) + if _, err := outf.Seek(cmdOffset, 0); err != nil { return err } // Read the load commands, looking for the LC_UUID cmd. If/when we // locate it, overwrite it with a new value produced by // uuidFromGoBuildId. - reader := loadCmdReader{next: int64(cmdOffset), - f: outf, order: exem.ByteOrder} + reader := imacho.NewLoadCmdUpdater(outf, exem.ByteOrder, cmdOffset) for i := uint32(0); i < exem.Ncmd; i++ { cmd, err := reader.Next() if err != nil { return err } - if cmd.Cmd == LC_UUID { + if cmd.Cmd == imacho.LC_UUID { var u uuidCmd if err := reader.ReadAt(0, &u); err != nil { return err