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