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