github.com/cloudwego/iasm@v0.2.0/obj/macho.go (about)

     1  //
     2  // Copyright 2024 CloudWeGo Authors
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package obj
    18  
    19  import (
    20      `encoding/binary`
    21      `io`
    22      `unsafe`
    23  )
    24  
    25  type MachOHeader struct {
    26      Magic      uint32
    27      CPUType    uint32
    28      CPUSubType uint32
    29      FileType   uint32
    30      CmdCount   uint32
    31      CmdSize    uint32
    32      Flags      uint32
    33      _          uint32
    34  }
    35  
    36  type SegmentCommand struct {
    37      Cmd          uint32
    38      Size         uint32
    39      Name         [16]byte
    40      VMAddr       uint64
    41      VMSize       uint64
    42      FileOffset   uint64
    43      FileSize     uint64
    44      MaxProtect   uint32
    45      InitProtect  uint32
    46      SectionCount uint32
    47      Flags        uint32
    48  }
    49  
    50  type SegmentSection struct {
    51      Name      [16]byte
    52      SegName   [16]byte
    53      Addr      uint64
    54      Size      uint64
    55      Offset    uint32
    56      Align     uint32
    57      RelOffset uint32
    58      RelCount  uint32
    59      Flags     uint32
    60      _         [3]uint32
    61  }
    62  
    63  type Registers struct {
    64      RAX    uint64
    65      RBX    uint64
    66      RCX    uint64
    67      RDX    uint64
    68      RDI    uint64
    69      RSI    uint64
    70      RBP    uint64
    71      RSP    uint64
    72      R8     uint64
    73      R9     uint64
    74      R10    uint64
    75      R11    uint64
    76      R12    uint64
    77      R13    uint64
    78      R14    uint64
    79      R15    uint64
    80      RIP    uint64
    81      RFLAGS uint64
    82      CS     uint64
    83      FS     uint64
    84      GS     uint64
    85  }
    86  
    87  type UnixThreadCommand struct {
    88      Cmd    uint32
    89      Size   uint32
    90      Flavor uint32
    91      Count  uint32
    92      Regs   Registers
    93  }
    94  
    95  const (
    96      _MH_MAGIC_64 = 0xfeedfacf
    97      _MH_EXECUTE  = 0x02
    98      _MH_NOUNDEFS = 0x01
    99  )
   100  
   101  const (
   102      _CPU_TYPE_I386  = 0x00000007
   103      _CPU_ARCH_ABI64 = 0x01000000
   104  )
   105  
   106  const (
   107      _CPU_SUBTYPE_LIB64    = 0x80000000
   108      _CPU_SUBTYPE_I386_ALL = 0x00000003
   109  )
   110  
   111  const (
   112      _LC_SEGMENT_64 = 0x19
   113      _LC_UNIXTHREAD = 0x05
   114  )
   115  
   116  const (
   117      _VM_PROT_READ    = 0x01
   118      _VM_PROT_WRITE   = 0x02
   119      _VM_PROT_EXECUTE = 0x04
   120  )
   121  
   122  const (
   123      _S_ATTR_SOME_INSTRUCTIONS = 0x00000400
   124      _S_ATTR_PURE_INSTRUCTIONS = 0x80000000
   125  )
   126  
   127  const (
   128      _x86_THREAD_STATE64          = 0x04
   129      _x86_EXCEPTION_STATE64_COUNT = 42
   130  )
   131  
   132  const (
   133      _MACHO_SIZE      = uint32(unsafe.Sizeof(MachOHeader{}))
   134      _SEGMENT_SIZE    = uint32(unsafe.Sizeof(SegmentCommand{}))
   135      _SECTION_SIZE    = uint32(unsafe.Sizeof(SegmentSection{}))
   136      _UNIXTHREAD_SIZE = uint32(unsafe.Sizeof(UnixThreadCommand{}))
   137  )
   138  
   139  const (
   140      _IMAGE_SIZE = 4096
   141      _IMAGE_BASE = 0x04000000
   142  )
   143  
   144  const (
   145      _HDR_SIZE  = _MACHO_SIZE + _SEGMENT_SIZE * 2 + _SECTION_SIZE + _UNIXTHREAD_SIZE
   146      _ZERO_SIZE = (_IMAGE_SIZE - _HDR_SIZE %_IMAGE_SIZE) % _IMAGE_SIZE
   147  )
   148  
   149  var (
   150      zeroBytes = [_ZERO_SIZE]byte{}
   151  )
   152  
   153  func assembleMachO(w io.Writer, code []byte, base uint64, entry uint64) error {
   154      var p0name [16]byte
   155      var txname [16]byte
   156      var tsname [16]byte
   157  
   158      /* segment names */
   159      copy(tsname[:], "__text")
   160      copy(txname[:], "__TEXT")
   161      copy(p0name[:], "__PAGEZERO")
   162  
   163      /* calculate size of code */
   164      clen := uint64(len(code))
   165      hlen := uint64(_HDR_SIZE + _ZERO_SIZE)
   166  
   167      /* Mach-O does not allow image base at zero */
   168      if base == 0 {
   169          base = _IMAGE_BASE
   170          entry += _IMAGE_BASE
   171      }
   172  
   173      /* Page-0 Segment */
   174      p0 := SegmentCommand {
   175          Cmd    : _LC_SEGMENT_64,
   176          Size   : _SEGMENT_SIZE,
   177          Name   : p0name,
   178          VMSize : base,
   179      }
   180  
   181      /* TEXT Segment */
   182      text := SegmentCommand {
   183          Cmd          : _LC_SEGMENT_64,
   184          Size         : _SEGMENT_SIZE + _SECTION_SIZE,
   185          Name         : txname,
   186          VMAddr       : base,
   187          VMSize       : hlen + clen,
   188          FileSize     : hlen + clen,
   189          MaxProtect   : _VM_PROT_READ | _VM_PROT_WRITE | _VM_PROT_EXECUTE,
   190          InitProtect  : _VM_PROT_READ | _VM_PROT_EXECUTE,
   191          SectionCount : 1,
   192      }
   193  
   194      /* __TEXT.__text section */
   195      tsec := SegmentSection {
   196          Name    : tsname,
   197          SegName : txname,
   198          Addr    : base + hlen,
   199          Size    : clen,
   200          Offset  : uint32(hlen),
   201          Flags   : _S_ATTR_SOME_INSTRUCTIONS | _S_ATTR_PURE_INSTRUCTIONS,
   202      }
   203  
   204      /* UNIX Thread Metadata */
   205      unix := UnixThreadCommand {
   206          Cmd    : _LC_UNIXTHREAD,
   207          Size   : _UNIXTHREAD_SIZE,
   208          Flavor : _x86_THREAD_STATE64,
   209          Count  : _x86_EXCEPTION_STATE64_COUNT,
   210          Regs   : Registers { RIP: hlen + entry },
   211      }
   212  
   213      /* Mach-O Header */
   214      macho := MachOHeader {
   215          Magic      : _MH_MAGIC_64,
   216          CPUType    : _CPU_ARCH_ABI64 | _CPU_TYPE_I386,
   217          CPUSubType : _CPU_SUBTYPE_LIB64 | _CPU_SUBTYPE_I386_ALL,
   218          FileType   : _MH_EXECUTE,
   219          CmdCount   : 3,
   220          CmdSize    : _SEGMENT_SIZE * 2 + _SECTION_SIZE + _UNIXTHREAD_SIZE,
   221          Flags      : _MH_NOUNDEFS,
   222      }
   223  
   224      /* write the headers */
   225      if err := binary.Write(w, binary.LittleEndian, &macho)     ; err != nil { return err }
   226      if err := binary.Write(w, binary.LittleEndian, &p0)        ; err != nil { return err }
   227      if err := binary.Write(w, binary.LittleEndian, &text)      ; err != nil { return err }
   228      if err := binary.Write(w, binary.LittleEndian, &tsec)      ; err != nil { return err }
   229      if err := binary.Write(w, binary.LittleEndian, &unix)      ; err != nil { return err }
   230      if err := binary.Write(w, binary.LittleEndian, &zeroBytes) ; err != nil { return err }
   231  
   232      /* write the code */
   233      if n, err := w.Write(code); err != nil {
   234          return err
   235      } else if n != len(code) {
   236          return io.ErrShortWrite
   237      } else {
   238          return nil
   239      }
   240  }