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