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