v / vlib / v2 / gen / x64 / coff.v
257 lines · 224 sloc · 6.31 KB · 1918e1160b29b144758ae6675de359f82939346b
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 coff_image_file_machine_amd64 = u16(0x8664)
10const coff_image_sym_class_external = u8(2)
11const coff_image_sym_class_static = u8(3)
12const coff_image_sym_dtype_function = u16(0x20)
13
14const coff_image_rel_amd64_rel32 = u16(0x0004)
15
16const coff_image_scn_cnt_code = u32(0x00000020)
17const coff_image_scn_cnt_initialized_data = u32(0x00000040)
18const coff_image_scn_align_8bytes = u32(0x00400000)
19const coff_image_scn_align_16bytes = u32(0x00500000)
20const coff_image_scn_mem_execute = u32(0x20000000)
21const coff_image_scn_mem_read = u32(0x40000000)
22const coff_image_scn_mem_write = u32(0x80000000)
23
24pub struct CoffObject {
25pub mut:
26 text_data []u8
27 data_data []u8
28 rodata []u8
29
30 symbols []CoffSymbol
31 str_table []u8
32 sym_by_name map[string]int
33
34 text_relocs []CoffRelocation
35}
36
37struct CoffSymbol {
38mut:
39 name string
40 name_off int
41 value u32
42 section i16
43 type_ u16
44 storage_class u8
45}
46
47struct CoffRelocation {
48 offset u32
49 sym_idx int
50 type_ u16
51}
52
53struct CoffSection {
54 name string
55 data []u8
56 relocs []CoffRelocation
57 characteristics u32
58}
59
60pub fn CoffObject.new() &CoffObject {
61 return &CoffObject{}
62}
63
64pub fn (mut c CoffObject) add_symbol(name string, value u64, is_func bool, section u8) int {
65 type_ := if is_func { coff_image_sym_dtype_function } else { u16(0) }
66 storage_class := coff_symbol_storage_class(name, section)
67 if i := c.sym_by_name[name] {
68 mut s := &c.symbols[i]
69 s.value = u32(value)
70 s.section = i16(section)
71 s.type_ = type_
72 s.storage_class = storage_class
73 return i
74 }
75
76 idx := c.symbols.len
77 c.symbols << CoffSymbol{
78 name: name
79 name_off: c.add_string_if_needed(name)
80 value: u32(value)
81 section: i16(section)
82 type_: type_
83 storage_class: storage_class
84 }
85 c.sym_by_name[name] = idx
86 return idx
87}
88
89pub fn (mut c CoffObject) add_undefined(name string) int {
90 if i := c.sym_by_name[name] {
91 return i
92 }
93
94 idx := c.symbols.len
95 c.symbols << CoffSymbol{
96 name: name
97 name_off: c.add_string_if_needed(name)
98 value: 0
99 section: 0
100 type_: 0
101 storage_class: coff_image_sym_class_external
102 }
103 c.sym_by_name[name] = idx
104 return idx
105}
106
107fn coff_symbol_storage_class(name string, section u8) u8 {
108 if section != 0 && name.starts_with('L_') {
109 return coff_image_sym_class_static
110 }
111 return coff_image_sym_class_external
112}
113
114pub fn (mut c CoffObject) add_text_reloc(offset int, sym_idx int, type_ u16) {
115 c.text_relocs << CoffRelocation{
116 offset: u32(offset)
117 sym_idx: sym_idx
118 type_: type_
119 }
120}
121
122fn (mut c CoffObject) add_string_if_needed(s string) int {
123 if s.len <= 8 {
124 return 0
125 }
126 off := 4 + c.str_table.len
127 c.str_table << s.bytes()
128 c.str_table << 0
129 return off
130}
131
132pub fn (mut c CoffObject) write(path string) {
133 sections := [
134 CoffSection{
135 name: '.text'
136 data: c.text_data
137 relocs: c.text_relocs
138 characteristics: coff_image_scn_cnt_code | coff_image_scn_mem_execute | coff_image_scn_mem_read | coff_image_scn_align_16bytes
139 },
140 CoffSection{
141 name: '.rdata'
142 data: c.rodata
143 characteristics: coff_image_scn_cnt_initialized_data | coff_image_scn_mem_read | coff_image_scn_align_8bytes
144 },
145 CoffSection{
146 name: '.data'
147 data: c.data_data
148 characteristics: coff_image_scn_cnt_initialized_data | coff_image_scn_mem_read | coff_image_scn_mem_write | coff_image_scn_align_8bytes
149 },
150 ]
151
152 header_size := 20
153 section_header_size := 40
154 reloc_entry_size := 10
155 mut current_offset := header_size + (sections.len * section_header_size)
156
157 mut raw_offsets := []int{len: sections.len}
158 for i, section in sections {
159 if section.data.len > 0 {
160 current_offset = align_int(current_offset, 4)
161 raw_offsets[i] = current_offset
162 current_offset += section.data.len
163 }
164 }
165
166 mut reloc_offsets := []int{len: sections.len}
167 for i, section in sections {
168 if section.relocs.len > 0 {
169 current_offset = align_int(current_offset, 4)
170 reloc_offsets[i] = current_offset
171 current_offset += section.relocs.len * reloc_entry_size
172 }
173 }
174
175 symbol_table_off := align_int(current_offset, 4)
176 string_table_size := 4 + c.str_table.len
177
178 mut buf := []u8{}
179 write_u16_le(mut buf, coff_image_file_machine_amd64)
180 write_u16_le(mut buf, u16(sections.len))
181 write_u32_le(mut buf, 0)
182 write_u32_le(mut buf, u32(symbol_table_off))
183 write_u32_le(mut buf, u32(c.symbols.len))
184 write_u16_le(mut buf, 0)
185 write_u16_le(mut buf, 0)
186
187 for i, section in sections {
188 write_fixed_string(mut buf, section.name, 8)
189 write_u32_le(mut buf, 0)
190 write_u32_le(mut buf, 0)
191 write_u32_le(mut buf, u32(section.data.len))
192 write_u32_le(mut buf, u32(raw_offsets[i]))
193 write_u32_le(mut buf, u32(reloc_offsets[i]))
194 write_u32_le(mut buf, 0)
195 nrelocs := coff_relocation_count_for_header(section) or { panic(err) }
196 write_u16_le(mut buf, nrelocs)
197 write_u16_le(mut buf, 0)
198 write_u32_le(mut buf, section.characteristics)
199 }
200
201 for i, section in sections {
202 if raw_offsets[i] == 0 {
203 continue
204 }
205 for buf.len < raw_offsets[i] {
206 buf << 0
207 }
208 buf << section.data
209 }
210
211 for i, section in sections {
212 if reloc_offsets[i] == 0 {
213 continue
214 }
215 for buf.len < reloc_offsets[i] {
216 buf << 0
217 }
218 for reloc in section.relocs {
219 write_u32_le(mut buf, reloc.offset)
220 write_u32_le(mut buf, u32(reloc.sym_idx))
221 write_u16_le(mut buf, reloc.type_)
222 }
223 }
224
225 for buf.len < symbol_table_off {
226 buf << 0
227 }
228 for sym in c.symbols {
229 c.write_symbol_name(mut buf, sym)
230 write_u32_le(mut buf, sym.value)
231 write_u16_le(mut buf, u16(sym.section))
232 write_u16_le(mut buf, sym.type_)
233 buf << sym.storage_class
234 buf << 0
235 }
236
237 write_u32_le(mut buf, u32(string_table_size))
238 buf << c.str_table
239
240 os.write_file_array(path, buf) or { panic(err) }
241}
242
243fn coff_relocation_count_for_header(section CoffSection) !u16 {
244 if section.relocs.len > 0xffff {
245 return error('COFF section ${section.name} has ${section.relocs.len} relocations; extended relocations are not supported')
246 }
247 return u16(section.relocs.len)
248}
249
250fn (c CoffObject) write_symbol_name(mut buf []u8, sym CoffSymbol) {
251 if sym.name.len <= 8 {
252 write_fixed_string(mut buf, sym.name, 8)
253 return
254 }
255 write_u32_le(mut buf, 0)
256 write_u32_le(mut buf, u32(sym.name_off))
257}
258