v / vlib / v2 / gen / arm64 / macho.v
285 lines · 248 sloc · 6.4 KB · 90c51ad2466514d5a103bd70cd0a3618ac1fa2da
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module arm64
6
7// import encoding.binary
8import os
9
10// Mach-O Constants for ARM64
11const mh_magic_64 = u32(0xfeedfacf)
12const cpu_type_arm64 = 0x0100000c
13const cpu_subtype_arm64_all = 0
14
15const lc_segment_64 = 0x19
16const lc_symtab = 0x2
17
18const arm64_reloc_branch26 = 2
19const arm64_reloc_page21 = 3
20const arm64_reloc_pageoff12 = 4
21const arm64_reloc_got_load_page21 = 5
22const arm64_reloc_got_load_pageoff12 = 6
23
24pub struct MachOObject {
25pub mut:
26 text_data []u8
27 str_data []u8
28 data_data []u8
29
30 relocs []RelocationInfo
31 symbols []Symbol
32 str_table []u8
33 sym_by_name map[string]int // symbol name → index in symbols
34}
35
36struct RelocationInfo {
37 addr int
38 sym_idx int
39 pcrel bool
40 length int
41 extern bool
42 type_ int
43}
44
45struct Symbol {
46mut:
47 name string
48 type_ u8
49 sect u8
50 desc u16
51 value u64
52 name_off int
53}
54
55pub fn MachOObject.new() &MachOObject {
56 mut m := &MachOObject{
57 str_table: [u8(0)]
58 }
59 return m
60}
61
62pub fn (mut m MachOObject) add_symbol(name string, addr u64, is_ext bool, sect u8) int {
63 typ := if is_ext { u8(0x0f) } else { u8(0x0e) } // N_SECT | N_EXT : N_SECT
64
65 // Check if symbol already exists (e.g., was added as undefined earlier)
66 if i := m.sym_by_name[name] {
67 mut s := &m.symbols[i]
68 s.type_ = typ
69 s.sect = sect
70 s.value = addr
71 return i
72 }
73
74 // Add new symbol
75 idx := m.symbols.len
76 name_off := m.str_table.len
77 unsafe { m.str_table.push_many(name.str, name.len) }
78 m.str_table << 0
79
80 m.symbols << Symbol{
81 name: name
82 type_: typ
83 sect: sect
84 desc: 0
85 value: addr
86 name_off: name_off
87 }
88 m.sym_by_name[name] = idx
89 return idx
90}
91
92pub fn (mut m MachOObject) add_undefined(name string) int {
93 // Check for any existing symbol with this name (defined or undefined)
94 if i := m.sym_by_name[name] {
95 return i
96 }
97
98 idx := m.symbols.len
99 name_off := m.str_table.len
100 unsafe { m.str_table.push_many(name.str, name.len) }
101 m.str_table << 0
102
103 m.symbols << Symbol{
104 name: name
105 type_: 0x01 // N_UNDF | N_EXT
106 sect: 0
107 desc: 0
108 value: 0
109 name_off: name_off
110 }
111 m.sym_by_name[name] = idx
112 return idx
113}
114
115pub fn (mut m MachOObject) add_reloc(addr int, sym_idx int, typ int, pcrel bool) {
116 m.relocs << RelocationInfo{
117 addr: addr
118 sym_idx: sym_idx // 0-based symbol table index
119 type_: typ
120 pcrel: pcrel
121 length: 2
122 extern: true
123 }
124}
125
126pub fn (mut m MachOObject) write(path string) {
127 mut buf := []u8{}
128
129 n_sects := 3
130 header_size := 32
131 seg_cmd_size := 72 + (80 * n_sects)
132 symtab_cmd_size := 24
133 load_cmds_size := seg_cmd_size + symtab_cmd_size
134
135 text_off := header_size + load_cmds_size
136 text_len := m.text_data.len
137 cstring_off := text_off + text_len
138 cstring_len := m.str_data.len
139 data_off := cstring_off + cstring_len
140 data_len := m.data_data.len
141
142 reloc_off := data_off + data_len
143 sym_off := reloc_off + (m.relocs.len * 8)
144 sym_len := m.symbols.len * 16
145 str_off := sym_off + sym_len
146 str_size := m.str_table.len
147
148 // 1. Header
149 write_u32_le(mut buf, mh_magic_64)
150 write_u32_le(mut buf, u32(cpu_type_arm64))
151 write_u32_le(mut buf, u32(cpu_subtype_arm64_all))
152 write_u32_le(mut buf, 1) // MH_OBJECT
153 write_u32_le(mut buf, 2) // ncmds
154 write_u32_le(mut buf, u32(load_cmds_size))
155 write_u32_le(mut buf, 0)
156 write_u32_le(mut buf, 0)
157
158 // 2. LC_SEGMENT_64
159 write_u32_le(mut buf, u32(lc_segment_64))
160 write_u32_le(mut buf, u32(seg_cmd_size))
161 for _ in 0 .. 16 {
162 buf << 0
163 }
164 write_u64_le(mut buf, 0)
165 write_u64_le(mut buf, u64(text_len + cstring_len + data_len))
166 write_u64_le(mut buf, u64(text_off))
167 write_u64_le(mut buf, u64(text_len + cstring_len + data_len))
168 write_u32_le(mut buf, 7)
169 write_u32_le(mut buf, 7)
170 write_u32_le(mut buf, u32(n_sects))
171 write_u32_le(mut buf, 0)
172
173 // Section 1: __text
174 write_string_fixed(mut buf, '__text', 16)
175 write_string_fixed(mut buf, '__TEXT', 16)
176 write_u64_le(mut buf, 0) // Addr 0
177 write_u64_le(mut buf, u64(text_len))
178 write_u32_le(mut buf, u32(text_off))
179 write_u32_le(mut buf, 4)
180 write_u32_le(mut buf, u32(reloc_off))
181 write_u32_le(mut buf, u32(m.relocs.len))
182 write_u32_le(mut buf, u32(0x80000400))
183 write_u32_le(mut buf, 0)
184 write_u32_le(mut buf, 0)
185 write_u32_le(mut buf, 0)
186
187 // Section 2: __cstring
188 write_string_fixed(mut buf, '__cstring', 16)
189 write_string_fixed(mut buf, '__TEXT', 16)
190 write_u64_le(mut buf, u64(text_len)) // Addr = text_len
191 write_u64_le(mut buf, u64(cstring_len))
192 write_u32_le(mut buf, u32(cstring_off))
193 write_u32_le(mut buf, 0)
194 write_u32_le(mut buf, 0)
195 write_u32_le(mut buf, 0)
196 write_u32_le(mut buf, 2) // S_CSTRING_LITERALS
197 write_u32_le(mut buf, 0)
198 write_u32_le(mut buf, 0)
199 write_u32_le(mut buf, 0)
200
201 // Section 3: __data
202 write_string_fixed(mut buf, '__data', 16)
203 write_string_fixed(mut buf, '__DATA', 16)
204 write_u64_le(mut buf, u64(text_len + cstring_len)) // Addr = text_len + cstring_len
205 write_u64_le(mut buf, u64(data_len))
206 write_u32_le(mut buf, u32(data_off))
207 write_u32_le(mut buf, 3) // align 8
208 write_u32_le(mut buf, 0)
209 write_u32_le(mut buf, 0)
210 write_u32_le(mut buf, 0)
211 write_u32_le(mut buf, 0)
212 write_u32_le(mut buf, 0)
213 write_u32_le(mut buf, 0)
214
215 // 3. LC_SYMTAB
216 write_u32_le(mut buf, u32(lc_symtab))
217 write_u32_le(mut buf, u32(symtab_cmd_size))
218 write_u32_le(mut buf, u32(sym_off))
219 write_u32_le(mut buf, u32(m.symbols.len))
220 write_u32_le(mut buf, u32(str_off))
221 write_u32_le(mut buf, u32(str_size))
222
223 buf << m.text_data
224 buf << m.str_data
225 buf << m.data_data
226
227 for r in m.relocs {
228 write_u32_le(mut buf, u32(r.addr))
229 mut info := u32(r.sym_idx)
230 if r.pcrel {
231 info |= (1 << 24)
232 }
233 info |= (u32(r.length) << 25)
234 if r.extern {
235 info |= (1 << 27)
236 }
237 info |= (u32(r.type_) << 28)
238 write_u32_le(mut buf, info)
239 }
240
241 for s in m.symbols {
242 write_u32_le(mut buf, u32(s.name_off))
243 buf << s.type_
244 buf << s.sect
245 write_u16_le(mut buf, s.desc)
246 write_u64_le(mut buf, s.value)
247 }
248
249 buf << m.str_table
250
251 os.write_file_array(path, buf) or { panic(err) }
252}
253
254fn write_u32_le(mut b []u8, v u32) {
255 b << u8(v)
256 b << u8(v >> 8)
257 b << u8(v >> 16)
258 b << u8(v >> 24)
259}
260
261fn write_u64_le(mut b []u8, v u64) {
262 b << u8(v)
263 b << u8(v >> 8)
264 b << u8(v >> 16)
265 b << u8(v >> 24)
266 b << u8(v >> 32)
267 b << u8(v >> 40)
268 b << u8(v >> 48)
269 b << u8(v >> 56)
270}
271
272fn write_u16_le(mut b []u8, v u16) {
273 b << u8(v)
274 b << u8(v >> 8)
275}
276
277fn write_string_fixed(mut b []u8, s string, len int) {
278 mut bytes := s.bytes()
279 for bytes.len < len {
280 bytes << 0
281 }
282 for i in 0 .. len {
283 b << bytes[i]
284 }
285}
286