Source file src/cmd/internal/macho/macho.go

     1  // Copyright 2024 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 macho provides functionalities to handle Mach-O
     6  // beyond the debug/macho package, for the toolchain.
     7  package macho
     8  
     9  import (
    10  	"debug/macho"
    11  	"encoding/binary"
    12  	"io"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	LC_SEGMENT                  = 0x1
    18  	LC_SYMTAB                   = 0x2
    19  	LC_SYMSEG                   = 0x3
    20  	LC_THREAD                   = 0x4
    21  	LC_UNIXTHREAD               = 0x5
    22  	LC_LOADFVMLIB               = 0x6
    23  	LC_IDFVMLIB                 = 0x7
    24  	LC_IDENT                    = 0x8
    25  	LC_FVMFILE                  = 0x9
    26  	LC_PREPAGE                  = 0xa
    27  	LC_DYSYMTAB                 = 0xb
    28  	LC_LOAD_DYLIB               = 0xc
    29  	LC_ID_DYLIB                 = 0xd
    30  	LC_LOAD_DYLINKER            = 0xe
    31  	LC_ID_DYLINKER              = 0xf
    32  	LC_PREBOUND_DYLIB           = 0x10
    33  	LC_ROUTINES                 = 0x11
    34  	LC_SUB_FRAMEWORK            = 0x12
    35  	LC_SUB_UMBRELLA             = 0x13
    36  	LC_SUB_CLIENT               = 0x14
    37  	LC_SUB_LIBRARY              = 0x15
    38  	LC_TWOLEVEL_HINTS           = 0x16
    39  	LC_PREBIND_CKSUM            = 0x17
    40  	LC_LOAD_WEAK_DYLIB          = 0x80000018
    41  	LC_SEGMENT_64               = 0x19
    42  	LC_ROUTINES_64              = 0x1a
    43  	LC_UUID                     = 0x1b
    44  	LC_RPATH                    = 0x8000001c
    45  	LC_CODE_SIGNATURE           = 0x1d
    46  	LC_SEGMENT_SPLIT_INFO       = 0x1e
    47  	LC_REEXPORT_DYLIB           = 0x8000001f
    48  	LC_LAZY_LOAD_DYLIB          = 0x20
    49  	LC_ENCRYPTION_INFO          = 0x21
    50  	LC_DYLD_INFO                = 0x22
    51  	LC_DYLD_INFO_ONLY           = 0x80000022
    52  	LC_LOAD_UPWARD_DYLIB        = 0x80000023
    53  	LC_VERSION_MIN_MACOSX       = 0x24
    54  	LC_VERSION_MIN_IPHONEOS     = 0x25
    55  	LC_FUNCTION_STARTS          = 0x26
    56  	LC_DYLD_ENVIRONMENT         = 0x27
    57  	LC_MAIN                     = 0x80000028
    58  	LC_DATA_IN_CODE             = 0x29
    59  	LC_SOURCE_VERSION           = 0x2A
    60  	LC_DYLIB_CODE_SIGN_DRS      = 0x2B
    61  	LC_ENCRYPTION_INFO_64       = 0x2C
    62  	LC_LINKER_OPTION            = 0x2D
    63  	LC_LINKER_OPTIMIZATION_HINT = 0x2E
    64  	LC_VERSION_MIN_TVOS         = 0x2F
    65  	LC_VERSION_MIN_WATCHOS      = 0x30
    66  	LC_VERSION_NOTE             = 0x31
    67  	LC_BUILD_VERSION            = 0x32
    68  	LC_DYLD_EXPORTS_TRIE        = 0x80000033
    69  	LC_DYLD_CHAINED_FIXUPS      = 0x80000034
    70  )
    71  
    72  // LoadCmd is macho.LoadCmd with its length, which is also
    73  // the load command header in the Mach-O file.
    74  type LoadCmd struct {
    75  	Cmd macho.LoadCmd
    76  	Len uint32
    77  }
    78  
    79  type LoadCmdReader struct {
    80  	offset, next int64
    81  	f            io.ReadSeeker
    82  	order        binary.ByteOrder
    83  }
    84  
    85  func NewLoadCmdReader(f io.ReadSeeker, order binary.ByteOrder, nextOffset int64) LoadCmdReader {
    86  	return LoadCmdReader{next: nextOffset, f: f, order: order}
    87  }
    88  
    89  func (r *LoadCmdReader) Next() (LoadCmd, error) {
    90  	var cmd LoadCmd
    91  
    92  	r.offset = r.next
    93  	if _, err := r.f.Seek(r.offset, 0); err != nil {
    94  		return cmd, err
    95  	}
    96  	if err := binary.Read(r.f, r.order, &cmd); err != nil {
    97  		return cmd, err
    98  	}
    99  	r.next = r.offset + int64(cmd.Len)
   100  	return cmd, nil
   101  }
   102  
   103  func (r LoadCmdReader) ReadAt(offset int64, data interface{}) error {
   104  	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
   105  		return err
   106  	}
   107  	return binary.Read(r.f, r.order, data)
   108  }
   109  
   110  func (r LoadCmdReader) Offset() int64 { return r.offset }
   111  
   112  type LoadCmdUpdater struct {
   113  	LoadCmdReader
   114  }
   115  
   116  func NewLoadCmdUpdater(f io.ReadWriteSeeker, order binary.ByteOrder, nextOffset int64) LoadCmdUpdater {
   117  	return LoadCmdUpdater{NewLoadCmdReader(f, order, nextOffset)}
   118  }
   119  
   120  func (u LoadCmdUpdater) WriteAt(offset int64, data interface{}) error {
   121  	if _, err := u.f.Seek(u.offset+offset, 0); err != nil {
   122  		return err
   123  	}
   124  	return binary.Write(u.f.(io.Writer), u.order, data)
   125  }
   126  
   127  func FileHeaderSize(f *macho.File) int64 {
   128  	offset := int64(unsafe.Sizeof(f.FileHeader))
   129  	if is64bit := f.Magic == macho.Magic64; is64bit {
   130  		// mach_header_64 has one extra uint32.
   131  		offset += 4
   132  	}
   133  	return offset
   134  }
   135  

View as plain text