1
0
mirror of https://github.com/golang/go synced 2024-11-13 12:40:26 -07:00

cmd/internal/macho: new package for handling mach-o files in toolchain

Currently the linker has some code handling and manipulating
Mach-O files. Specifically, it augments the debug/macho package
with file offset and length, so the content can be handled or
updated easily with the file.

Move this code to an internal package, so it can be used by other
part of the toolchain, e.g. buildid computation.

For #68678.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13
Change-Id: I2311af0a06441b7fd887ca5c6ed9e6fc44670a16
Reviewed-on: https://go-review.googlesource.com/c/go/+/618596
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Cherry Mui 2024-10-07 11:20:15 -04:00
parent 20ed603118
commit f2d9f5ffca
5 changed files with 180 additions and 145 deletions

View File

@ -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",

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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