v / vlib / v2 / gen / x64 / macho.v
284 lines · 250 sloc · 6.38 KB · 81a5657604ec6da99c25e26546870c6888d6fdde
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 x64
6
7import os
8
9const macho_mh_magic_64 = u32(0xfeedfacf)
10const macho_cpu_type_x86_64 = 0x01000007
11const macho_cpu_subtype_x86_64_all = 3
12const macho_mh_object = 1
13const macho_lc_segment_64 = 0x19
14const macho_lc_symtab = 0x2
15
16const x86_64_reloc_signed = 1
17const x86_64_reloc_branch = 2
18const x86_64_reloc_got_load = 3
19const x86_64_reloc_got = 4
20
21pub struct MachOObject {
22pub mut:
23 text_data []u8
24 data_data []u8
25 rodata []u8
26
27 relocs []MachORelocationInfo
28 symbols []MachOSymbol
29 str_table []u8
30 sym_by_name map[string]int
31}
32
33struct MachORelocationInfo {
34 addr int
35 sym_idx int
36 pcrel bool
37 length int
38 extern bool
39 type_ int
40}
41
42struct MachOSymbol {
43mut:
44 name string
45 type_ u8
46 sect u8
47 desc u16
48 value u64
49 name_off int
50}
51
52pub fn MachOObject.new() &MachOObject {
53 return &MachOObject{
54 str_table: [u8(0)]
55 }
56}
57
58pub fn (mut m MachOObject) add_symbol(name string, value u64, is_ext bool, sect u8) int {
59 typ := if is_ext { u8(0x0f) } else { u8(0x0e) }
60 if i := m.sym_by_name[name] {
61 mut s := &m.symbols[i]
62 s.type_ = typ
63 s.sect = sect
64 s.value = value
65 return i
66 }
67
68 idx := m.symbols.len
69 name_off := m.add_string(name)
70 m.symbols << MachOSymbol{
71 name: name
72 type_: typ
73 sect: sect
74 desc: 0
75 value: value
76 name_off: name_off
77 }
78 m.sym_by_name[name] = idx
79 return idx
80}
81
82pub fn (mut m MachOObject) add_undefined(name string) int {
83 if i := m.sym_by_name[name] {
84 return i
85 }
86
87 idx := m.symbols.len
88 name_off := m.add_string(name)
89 m.symbols << MachOSymbol{
90 name: name
91 type_: 0x01 // N_UNDF | N_EXT
92 sect: 0
93 desc: 0
94 value: 0
95 name_off: name_off
96 }
97 m.sym_by_name[name] = idx
98 return idx
99}
100
101pub fn (mut m MachOObject) add_reloc(addr int, sym_idx int, typ int, pcrel bool, length int) {
102 m.relocs << MachORelocationInfo{
103 addr: addr
104 sym_idx: sym_idx
105 type_: typ
106 pcrel: pcrel
107 length: length
108 extern: true
109 }
110}
111
112fn (mut m MachOObject) add_string(s string) int {
113 off := m.str_table.len
114 m.str_table << s.bytes()
115 m.str_table << 0
116 return off
117}
118
119pub fn (mut m MachOObject) write(path string) {
120 mut buf := []u8{}
121
122 n_sects := 3
123 header_size := 32
124 seg_cmd_size := 72 + (80 * n_sects)
125 symtab_cmd_size := 24
126 load_cmds_size := seg_cmd_size + symtab_cmd_size
127
128 text_off := align_int(header_size + load_cmds_size, 16)
129 text_len := m.text_data.len
130 rodata_off := align_int(text_off + text_len, 8)
131 rodata_len := m.rodata.len
132 data_off := align_int(rodata_off + rodata_len, 8)
133 data_len := m.data_data.len
134
135 text_addr := 0
136 rodata_addr := align_int(text_addr + text_len, 8)
137 data_addr := align_int(rodata_addr + rodata_len, 8)
138 total_size := data_addr + data_len
139
140 reloc_off := align_int(data_off + data_len, 8)
141 sym_off := align_int(reloc_off + (m.relocs.len * 8), 8)
142 sym_len := m.symbols.len * 16
143 str_off := sym_off + sym_len
144 str_size := m.str_table.len
145
146 write_u32_le(mut buf, macho_mh_magic_64)
147 write_u32_le(mut buf, u32(macho_cpu_type_x86_64))
148 write_u32_le(mut buf, u32(macho_cpu_subtype_x86_64_all))
149 write_u32_le(mut buf, macho_mh_object)
150 write_u32_le(mut buf, 2)
151 write_u32_le(mut buf, u32(load_cmds_size))
152 write_u32_le(mut buf, 0)
153 write_u32_le(mut buf, 0)
154
155 write_u32_le(mut buf, macho_lc_segment_64)
156 write_u32_le(mut buf, u32(seg_cmd_size))
157 write_fixed_string(mut buf, '', 16)
158 write_u64_le(mut buf, 0)
159 write_u64_le(mut buf, u64(total_size))
160 write_u64_le(mut buf, u64(text_off))
161 write_u64_le(mut buf, u64(data_off + data_len - text_off))
162 write_u32_le(mut buf, 7)
163 write_u32_le(mut buf, 7)
164 write_u32_le(mut buf, u32(n_sects))
165 write_u32_le(mut buf, 0)
166
167 write_fixed_string(mut buf, '__text', 16)
168 write_fixed_string(mut buf, '__TEXT', 16)
169 write_u64_le(mut buf, u64(text_addr))
170 write_u64_le(mut buf, u64(text_len))
171 write_u32_le(mut buf, u32(text_off))
172 write_u32_le(mut buf, 4)
173 write_u32_le(mut buf, u32(reloc_off))
174 write_u32_le(mut buf, u32(m.relocs.len))
175 write_u32_le(mut buf, u32(0x80000400))
176 write_u32_le(mut buf, 0)
177 write_u32_le(mut buf, 0)
178 write_u32_le(mut buf, 0)
179
180 write_fixed_string(mut buf, '__const', 16)
181 write_fixed_string(mut buf, '__TEXT', 16)
182 write_u64_le(mut buf, u64(rodata_addr))
183 write_u64_le(mut buf, u64(rodata_len))
184 write_u32_le(mut buf, u32(rodata_off))
185 write_u32_le(mut buf, 3)
186 write_u32_le(mut buf, 0)
187 write_u32_le(mut buf, 0)
188 write_u32_le(mut buf, 0)
189 write_u32_le(mut buf, 0)
190 write_u32_le(mut buf, 0)
191 write_u32_le(mut buf, 0)
192
193 write_fixed_string(mut buf, '__data', 16)
194 write_fixed_string(mut buf, '__DATA', 16)
195 write_u64_le(mut buf, u64(data_addr))
196 write_u64_le(mut buf, u64(data_len))
197 write_u32_le(mut buf, u32(data_off))
198 write_u32_le(mut buf, 3)
199 write_u32_le(mut buf, 0)
200 write_u32_le(mut buf, 0)
201 write_u32_le(mut buf, 0)
202 write_u32_le(mut buf, 0)
203 write_u32_le(mut buf, 0)
204 write_u32_le(mut buf, 0)
205
206 write_u32_le(mut buf, macho_lc_symtab)
207 write_u32_le(mut buf, u32(symtab_cmd_size))
208 write_u32_le(mut buf, u32(sym_off))
209 write_u32_le(mut buf, u32(m.symbols.len))
210 write_u32_le(mut buf, u32(str_off))
211 write_u32_le(mut buf, u32(str_size))
212
213 for buf.len < text_off {
214 buf << 0
215 }
216 buf << m.text_data
217 for buf.len < rodata_off {
218 buf << 0
219 }
220 buf << m.rodata
221 for buf.len < data_off {
222 buf << 0
223 }
224 buf << m.data_data
225
226 for buf.len < reloc_off {
227 buf << 0
228 }
229 for r in m.relocs {
230 write_u32_le(mut buf, u32(r.addr))
231 mut info := u32(r.sym_idx)
232 if r.pcrel {
233 info |= 1 << 24
234 }
235 info |= u32(r.length) << 25
236 if r.extern {
237 info |= 1 << 27
238 }
239 info |= u32(r.type_) << 28
240 write_u32_le(mut buf, info)
241 }
242
243 for buf.len < sym_off {
244 buf << 0
245 }
246 for s in m.symbols {
247 write_u32_le(mut buf, u32(s.name_off))
248 buf << s.type_
249 buf << s.sect
250 write_u16_le(mut buf, s.desc)
251 write_u64_le(mut buf, m.symbol_value(s, rodata_addr, data_addr))
252 }
253
254 buf << m.str_table
255
256 os.write_file_array(path, buf) or { panic(err) }
257}
258
259fn (m MachOObject) symbol_value(s MachOSymbol, rodata_addr int, data_addr int) u64 {
260 return match s.sect {
261 1 { s.value }
262 2 { u64(rodata_addr) + s.value }
263 3 { u64(data_addr) + s.value }
264 else { s.value }
265 }
266}
267
268fn align_int(value int, alignment int) int {
269 if alignment <= 1 || value % alignment == 0 {
270 return value
271 }
272 return value + (alignment - (value % alignment))
273}
274
275fn write_fixed_string(mut b []u8, s string, len int) {
276 bytes := s.bytes()
277 for i in 0 .. len {
278 if i < bytes.len {
279 b << bytes[i]
280 } else {
281 b << 0
282 }
283 }
284}
285