v / vlib / v2 / gen / x64 / elf.v
409 lines · 346 sloc · 8.49 KB · e8810a671233f1d2072d70ab8e17c3b1dad5e1e5
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
9// ELF64 Constants
10const ei_mag0 = 0x7f
11const ei_mag1 = 0x45 // 'E'
12const ei_mag2 = 0x4c // 'L'
13const ei_mag3 = 0x46 // 'F'
14
15const elfclass64 = 2
16const elfdata2lsb = 1
17const ev_current = 1
18const et_rel = 1 // Relocatable file
19const et_exec = 2 // Executable file
20const em_x86_64 = 62
21
22const pt_load = 1
23
24const pf_x = 0x1
25const pf_w = 0x2
26const pf_r = 0x4
27
28const sht_progbits = 1
29const sht_symtab = 2
30const sht_strtab = 3
31const sht_rela = 4
32const sht_nobits = 8
33
34const shf_write = 0x1
35const shf_alloc = 0x2
36const shf_execinstr = 0x4
37
38// Relocation Types for x86_64
39const r_x86_64_64 = 1
40const r_x86_64_pc32 = 2
41const r_x86_64_plt32 = 4
42
43pub struct ElfObject {
44pub mut:
45 text_data []u8
46 data_data []u8
47 rodata []u8
48
49 // Symbol Table
50 symbols []ElfSymbol
51 str_table []u8
52
53 // Relocations
54 text_relocs []ElfRela
55
56 // Section Names
57 shstr_table []u8
58}
59
60struct ElfSymbol {
61pub mut:
62 name string
63 name_idx int
64 info u8
65 shndx u16
66 value u64
67 size u64
68}
69
70struct ElfRela {
71 offset u64
72 info u64
73 addend i64
74}
75
76pub fn ElfObject.new() &ElfObject {
77 mut e := &ElfObject{
78 str_table: [u8(0)] // Starts with null byte
79 shstr_table: [u8(0)]
80 }
81 // Add null symbol
82 e.symbols << ElfSymbol{
83 name: ''
84 shndx: 0
85 }
86 return e
87}
88
89pub fn (mut e ElfObject) add_symbol(name string, value u64, is_func bool, shndx u16) int {
90 // STB_GLOBAL (1) << 4 | STT_FUNC (2) or STT_OBJECT (1) or STT_NOTYPE (0)
91 type_ := if is_func { u8(2) } else { u8(1) } // Func : Object
92 bind := u8(1) // Global
93 info := (bind << 4) | type_
94
95 for i := 1; i < e.symbols.len; i++ {
96 if e.symbols[i].name != name {
97 continue
98 }
99 mut s := &e.symbols[i]
100 s.info = info
101 s.shndx = shndx
102 s.value = value
103 return i
104 }
105
106 idx := e.symbols.len
107 name_off := e.add_string(name)
108
109 e.symbols << ElfSymbol{
110 name: name
111 name_idx: name_off
112 info: info
113 shndx: shndx
114 value: value
115 }
116 return idx
117}
118
119pub fn (mut e ElfObject) add_undefined(name string) int {
120 for i := 1; i < e.symbols.len; i++ {
121 if e.symbols[i].name == name {
122 return i
123 }
124 }
125
126 idx := e.symbols.len
127 name_off := e.add_string(name)
128 // Bind Global (1), Type NoType (0)
129 info := u8(0x10)
130
131 e.symbols << ElfSymbol{
132 name: name
133 name_idx: name_off
134 info: info
135 shndx: 0 // Undefined
136 value: 0
137 }
138 return idx
139}
140
141pub fn (mut e ElfObject) add_text_reloc(offset u64, sym_idx int, type_ int, addend i64) {
142 // info = (sym_idx << 32) | type
143 info := (u64(sym_idx) << 32) | u64(type_)
144 e.text_relocs << ElfRela{
145 offset: offset
146 info: info
147 addend: addend
148 }
149}
150
151fn (mut e ElfObject) add_string(s string) int {
152 off := e.str_table.len
153 e.str_table << s.bytes()
154 e.str_table << 0
155 return off
156}
157
158fn (mut e ElfObject) add_sh_string(s string) int {
159 off := e.shstr_table.len
160 e.shstr_table << s.bytes()
161 e.shstr_table << 0
162 return off
163}
164
165pub fn (mut e ElfObject) write(path string) {
166 mut buf := []u8{}
167
168 // --- 1. Prepare Sections ---
169 // Indices:
170 // 0: Null
171 // 1: .text
172 // 2: .data
173 // 3: .rodata (strings)
174 // 4: .symtab
175 // 5: .strtab
176 // 6: .rela.text
177 // 7: .shstrtab
178
179 // Prepare section names in .shstrtab
180 off_text_name := e.add_sh_string('.text')
181 off_data_name := e.add_sh_string('.data')
182 off_rodata_name := e.add_sh_string('.rodata')
183 off_symtab_name := e.add_sh_string('.symtab')
184 off_strtab_name := e.add_sh_string('.strtab')
185 off_rela_text_name := e.add_sh_string('.rela.text')
186 off_shstrtab_name := e.add_sh_string('.shstrtab')
187
188 // Calculate Offsets
189 ehdr_size := 64
190 shdr_entry_size := 64
191 num_sections := 8
192
193 mut current_offset := ehdr_size
194
195 // .text (Align 16)
196 if current_offset % 16 != 0 {
197 current_offset += (16 - (current_offset % 16))
198 }
199 off_text := current_offset
200 current_offset += e.text_data.len
201
202 // .data (Align 8)
203 if current_offset % 8 != 0 {
204 current_offset += (8 - (current_offset % 8))
205 }
206 off_data := current_offset
207 current_offset += e.data_data.len
208
209 // .rodata (Align 4)
210 if current_offset % 4 != 0 {
211 current_offset += (4 - (current_offset % 4))
212 }
213 off_rodata := current_offset
214 current_offset += e.rodata.len
215
216 // .symtab (Align 8)
217 if current_offset % 8 != 0 {
218 current_offset += (8 - (current_offset % 8))
219 }
220 off_symtab := current_offset
221 size_symtab := e.symbols.len * 24
222 current_offset += size_symtab
223
224 // .strtab (Align 1)
225 off_strtab := current_offset
226 current_offset += e.str_table.len
227
228 // .rela.text (Align 8)
229 if current_offset % 8 != 0 {
230 current_offset += (8 - (current_offset % 8))
231 }
232 off_rela_text := current_offset
233 size_rela_text := e.text_relocs.len * 24
234 current_offset += size_rela_text
235
236 // .shstrtab (Align 1)
237 off_shstrtab := current_offset
238 current_offset += e.shstr_table.len
239
240 // Section Headers (Align 8)
241 if current_offset % 8 != 0 {
242 current_offset += (8 - (current_offset % 8))
243 }
244 off_shdrs := current_offset
245
246 // --- 2. Write ELF Header ---
247 buf << u8(ei_mag0)
248 buf << u8(ei_mag1)
249 buf << u8(ei_mag2)
250 buf << u8(ei_mag3)
251 buf << u8(elfclass64)
252 buf << u8(elfdata2lsb)
253 buf << u8(ev_current)
254 buf << u8(0) // ABI System V
255 buf << u8(0) // ABI Version
256 for _ in 0 .. 7 {
257 buf << 0
258 } // Pad
259
260 write_u16_le(mut buf, et_rel)
261 write_u16_le(mut buf, em_x86_64)
262 write_u32_le(mut buf, ev_current)
263 write_u64_le(mut buf, 0) // Entry
264 write_u64_le(mut buf, 0) // Phdr off
265 write_u64_le(mut buf, u64(off_shdrs))
266 write_u32_le(mut buf, 0) // Flags
267 write_u16_le(mut buf, u16(ehdr_size))
268 write_u16_le(mut buf, 0) // Phdr entry size
269 write_u16_le(mut buf, 0) // Phdr num
270 write_u16_le(mut buf, u16(shdr_entry_size))
271 write_u16_le(mut buf, u16(num_sections))
272 write_u16_le(mut buf, 7) // Shstrndx
273
274 // --- 3. Write Data with Padding ---
275
276 // Pad to Text
277 for buf.len < off_text {
278 buf << 0
279 }
280 buf << e.text_data
281
282 // Pad to Data
283 for buf.len < off_data {
284 buf << 0
285 }
286 buf << e.data_data
287
288 // Pad to Rodata
289 for buf.len < off_rodata {
290 buf << 0
291 }
292 buf << e.rodata
293
294 // Pad to Symtab
295 for buf.len < off_symtab {
296 buf << 0
297 }
298 for sym in e.symbols {
299 write_u32_le(mut buf, u32(sym.name_idx))
300 buf << sym.info
301 buf << 0 // other
302 write_u16_le(mut buf, sym.shndx)
303 write_u64_le(mut buf, sym.value)
304 write_u64_le(mut buf, sym.size)
305 }
306
307 // Strtab
308 for buf.len < off_strtab {
309 buf << 0
310 }
311 buf << e.str_table
312
313 // Rela Text
314 for buf.len < off_rela_text {
315 buf << 0
316 }
317 for r in e.text_relocs {
318 write_u64_le(mut buf, r.offset)
319 write_u64_le(mut buf, r.info)
320 write_u64_le(mut buf, u64(r.addend))
321 }
322
323 // Shstrtab
324 for buf.len < off_shstrtab {
325 buf << 0
326 }
327 buf << e.shstr_table
328
329 // --- 4. Write Section Headers ---
330 for buf.len < off_shdrs {
331 buf << 0
332 }
333
334 // 0: Null
335 write_shdr(mut buf, 0, 0, 0, 0, 0, 0, 0, 0, 0)
336
337 // 1: .text
338 write_shdr(mut buf, u32(off_text_name), sht_progbits, shf_alloc | shf_execinstr, u64(off_text),
339 u64(e.text_data.len), 0, 0, 16, 0)
340
341 // 2: .data
342 write_shdr(mut buf, u32(off_data_name), sht_progbits, shf_alloc | shf_write, u64(off_data),
343 u64(e.data_data.len), 0, 0, 8, 0)
344
345 // 3: .rodata
346 write_shdr(mut buf, u32(off_rodata_name), sht_progbits, shf_alloc, u64(off_rodata),
347 u64(e.rodata.len), 0, 0, 4, 0)
348
349 // 4: .symtab (EntSize = 24)
350 mut first_global := 1
351 for i, s in e.symbols {
352 if (s.info >> 4) == 1 { // STB_GLOBAL
353 first_global = i
354 break
355 }
356 }
357 write_shdr(mut buf, u32(off_symtab_name), sht_symtab, 0, u64(off_symtab), u64(size_symtab), 5,
358 u32(first_global), 8, 24)
359
360 // 5: .strtab
361 write_shdr(mut buf, u32(off_strtab_name), sht_strtab, 0, u64(off_strtab), u64(e.str_table.len),
362 0, 0, 1, 0)
363
364 // 6: .rela.text (EntSize = 24)
365 write_shdr(mut buf, u32(off_rela_text_name), sht_rela, 0, u64(off_rela_text),
366 u64(size_rela_text), 4, 1, 8, 24)
367
368 // 7: .shstrtab
369 write_shdr(mut buf, u32(off_shstrtab_name), sht_strtab, 0, u64(off_shstrtab),
370 u64(e.shstr_table.len), 0, 0, 1, 0)
371
372 os.write_file_array(path, buf) or { panic(err) }
373}
374
375fn write_shdr(mut b []u8, name u32, type_ u32, flags u64, off u64, size u64, link u32, info u32, align u64, entsize u64) {
376 write_u32_le(mut b, name)
377 write_u32_le(mut b, type_)
378 write_u64_le(mut b, flags)
379 write_u64_le(mut b, 0) // Addr
380 write_u64_le(mut b, off)
381 write_u64_le(mut b, size)
382 write_u32_le(mut b, link)
383 write_u32_le(mut b, info)
384 write_u64_le(mut b, align)
385 write_u64_le(mut b, entsize)
386}
387
388fn write_u32_le(mut b []u8, v u32) {
389 b << u8(v)
390 b << u8(v >> 8)
391 b << u8(v >> 16)
392 b << u8(v >> 24)
393}
394
395fn write_u64_le(mut b []u8, v u64) {
396 b << u8(v)
397 b << u8(v >> 8)
398 b << u8(v >> 16)
399 b << u8(v >> 24)
400 b << u8(v >> 32)
401 b << u8(v >> 40)
402 b << u8(v >> 48)
403 b << u8(v >> 56)
404}
405
406fn write_u16_le(mut b []u8, v u16) {
407 b << u8(v)
408 b << u8(v >> 8)
409}
410