v / vlib / v2 / gen / x64 / object.v
249 lines · 222 sloc · 5.56 KB · e78b7311ad580c800e5a3a0bd0afe3876e609685
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 encoding.binary
8
9pub enum ObjectFormat {
10 elf
11 macho
12 coff
13}
14
15enum ObjectSection {
16 text
17 data
18 rodata
19}
20
21fn (format ObjectFormat) section_index(section ObjectSection) u8 {
22 return match format {
23 .elf {
24 match section {
25 .text { 1 }
26 .data { 2 }
27 .rodata { 3 }
28 }
29 }
30 .macho {
31 match section {
32 .text { 1 }
33 .rodata { 2 }
34 .data { 3 }
35 }
36 }
37 .coff {
38 match section {
39 .text { 1 }
40 .rodata { 2 }
41 .data { 3 }
42 }
43 }
44 }
45}
46
47fn (format ObjectFormat) symbol_name(name string) string {
48 if format == .macho && !name.starts_with('L_') {
49 return '_' + name
50 }
51 return name
52}
53
54fn (mut g Gen) text_len() int {
55 return match g.obj_format {
56 .elf { g.elf.text_data.len }
57 .macho { g.macho.text_data.len }
58 .coff { g.coff.text_data.len }
59 }
60}
61
62fn (mut g Gen) data_len() int {
63 return match g.obj_format {
64 .elf { g.elf.data_data.len }
65 .macho { g.macho.data_data.len }
66 .coff { g.coff.data_data.len }
67 }
68}
69
70fn (mut g Gen) rodata_len() int {
71 return match g.obj_format {
72 .elf { g.elf.rodata.len }
73 .macho { g.macho.rodata.len }
74 .coff { g.coff.rodata.len }
75 }
76}
77
78fn (mut g Gen) add_data_byte(b u8) {
79 match g.obj_format {
80 .elf { g.elf.data_data << b }
81 .macho { g.macho.data_data << b }
82 .coff { g.coff.data_data << b }
83 }
84}
85
86fn (mut g Gen) add_data(bytes []u8) {
87 match g.obj_format {
88 .elf { g.elf.data_data << bytes }
89 .macho { g.macho.data_data << bytes }
90 .coff { g.coff.data_data << bytes }
91 }
92}
93
94fn (mut g Gen) add_rodata_byte(b u8) {
95 match g.obj_format {
96 .elf { g.elf.rodata << b }
97 .macho { g.macho.rodata << b }
98 .coff { g.coff.rodata << b }
99 }
100}
101
102fn (mut g Gen) add_rodata(bytes []u8) {
103 match g.obj_format {
104 .elf { g.elf.rodata << bytes }
105 .macho { g.macho.rodata << bytes }
106 .coff { g.coff.rodata << bytes }
107 }
108}
109
110fn (mut g Gen) add_symbol(name string, value u64, is_func bool, section ObjectSection) int {
111 sym_name := g.obj_format.symbol_name(name)
112 sect := g.obj_format.section_index(section)
113 return match g.obj_format {
114 .elf { g.elf.add_symbol(sym_name, value, is_func, u16(sect)) }
115 .macho { g.macho.add_symbol(sym_name, value, !sym_name.starts_with('L_'), sect) }
116 .coff { g.coff.add_symbol(sym_name, value, is_func, sect) }
117 }
118}
119
120fn (mut g Gen) add_undefined(name string) int {
121 sym_name := g.obj_format.symbol_name(name)
122 return match g.obj_format {
123 .elf { g.elf.add_undefined(sym_name) }
124 .macho { g.macho.add_undefined(sym_name) }
125 .coff { g.coff.add_undefined(sym_name) }
126 }
127}
128
129fn (mut g Gen) add_call_reloc(sym_idx int) {
130 offset := g.text_len()
131 match g.obj_format {
132 .elf { g.elf.add_text_reloc(u64(offset), sym_idx, r_x86_64_plt32, -4) }
133 .macho { g.macho.add_reloc(offset, sym_idx, x86_64_reloc_branch, true, 2) }
134 .coff { g.coff.add_text_reloc(offset, sym_idx, coff_image_rel_amd64_rel32) }
135 }
136}
137
138fn (mut g Gen) add_rip_reloc(sym_idx int) {
139 offset := g.text_len()
140 match g.obj_format {
141 .elf { g.elf.add_text_reloc(u64(offset), sym_idx, r_x86_64_pc32, -4) }
142 .macho { g.macho.add_reloc(offset, sym_idx, x86_64_reloc_signed, true, 2) }
143 .coff { g.coff.add_text_reloc(offset, sym_idx, coff_image_rel_amd64_rel32) }
144 }
145}
146
147fn (mut g Gen) add_macho_got_load_reloc(sym_idx int) {
148 offset := g.text_len()
149 if g.obj_format == .macho {
150 g.macho.add_reloc(offset, sym_idx, x86_64_reloc_got_load, true, 2)
151 return
152 }
153 x64_unsupported('Mach-O GOT_LOAD relocation requested for ${g.obj_format}')
154}
155
156fn (mut g Gen) emit(b u8) {
157 match g.obj_format {
158 .elf { g.elf.text_data << b }
159 .macho { g.macho.text_data << b }
160 .coff { g.coff.text_data << b }
161 }
162}
163
164fn (mut g Gen) emit_u32(v u32) {
165 g.emit(u8(v))
166 g.emit(u8(v >> 8))
167 g.emit(u8(v >> 16))
168 g.emit(u8(v >> 24))
169}
170
171fn (mut g Gen) emit_u64(v u64) {
172 g.emit_u32(u32(v))
173 g.emit_u32(u32(v >> 32))
174}
175
176fn (mut g Gen) write_u32(off int, v u32) {
177 match g.obj_format {
178 .elf { binary.little_endian_put_u32(mut g.elf.text_data[off..off + 4], v) }
179 .macho { binary.little_endian_put_u32(mut g.macho.text_data[off..off + 4], v) }
180 .coff { binary.little_endian_put_u32(mut g.coff.text_data[off..off + 4], v) }
181 }
182}
183
184pub fn (mut g Gen) write_file(path string) {
185 if msg := g.unsupported_external_symbol_message() {
186 x64_abort_with_diagnostic(msg)
187 }
188 match g.obj_format {
189 .elf { g.elf.write(path) }
190 .macho { g.macho.write(path) }
191 .coff { g.coff.write(path) }
192 }
193}
194
195fn (g Gen) unsupported_external_symbol_message() ?string {
196 for raw_name in g.undefined_external_symbol_names() {
197 if msg := unsupported_external_symbol_message_for_name(g.obj_format, raw_name,
198 'needed while preparing native x64 output')
199 {
200 return msg
201 }
202 }
203 return none
204}
205
206fn (g Gen) undefined_external_symbol_names() []string {
207 mut names := []string{}
208 match g.obj_format {
209 .elf {
210 for sym in g.elf.symbols {
211 if sym.name != '' && sym.shndx == 0 {
212 names << sym.name
213 }
214 }
215 }
216 .macho {
217 for sym in g.macho.symbols {
218 if sym.name != '' && sym.sect == 0 && sym.type_ == 0x01 {
219 names << sym.name
220 }
221 }
222 }
223 .coff {
224 for sym in g.coff.symbols {
225 if sym.name != '' && sym.section == 0
226 && sym.storage_class == coff_image_sym_class_external {
227 names << sym.name
228 }
229 }
230 }
231 }
232
233 return names
234}
235
236pub fn (mut g Gen) link_executable(path string) ! {
237 match g.obj_format {
238 .coff {
239 if g.abi != .windows {
240 return error('x64 PE linking requires Windows ABI code generation')
241 }
242 mut linker := PeLinker.new(g.coff)
243 linker.write(path)!
244 }
245 else {
246 return error('x64 built-in executable linking is only implemented for COFF')
247 }
248 }
249}
250