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 }