v / vlib / v2 / gen / x64 / pe.v
2115 lines · 1984 sloc · 80.38 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
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 pe_dos_stub_size = 0x80
10const pe_signature = u32(0x00004550)
11const pe_optional_header64_magic = u16(0x20b)
12const pe_file_alignment = 0x200
13const pe_section_alignment = 0x1000
14const pe_image_base = u64(0x140000000)
15const pe_size_of_optional_header64 = u16(0xf0)
16const pe_number_of_rva_and_sizes = 16
17
18// These optional-header values are local policy defaults for the current
19// minimal linker, not requirements imposed by the Microsoft PE/COFF spec.
20const pe_linker_major_version = u8(0)
21const pe_linker_minor_version = u8(1)
22const pe_major_operating_system_version = u16(6)
23const pe_minor_operating_system_version = u16(0)
24const pe_major_subsystem_version = u16(6)
25const pe_minor_subsystem_version = u16(0)
26const pe_size_of_stack_reserve = u64(0x100000)
27const pe_size_of_stack_commit = u64(0x1000)
28const pe_size_of_heap_reserve = u64(0x100000)
29const pe_size_of_heap_commit = u64(0x1000)
30
31const pe_image_file_relocs_stripped = u16(0x0001)
32const pe_image_file_executable_image = u16(0x0002)
33const pe_image_file_large_address_aware = u16(0x0020)
34const pe_image_subsystem_windows_cui = u16(3)
35const pe_dll_characteristics_nx_compat = u16(0x0100)
36
37const pe_image_scn_cnt_code = u32(0x00000020)
38const pe_image_scn_cnt_initialized_data = u32(0x00000040)
39const pe_image_scn_mem_execute = u32(0x20000000)
40const pe_image_scn_mem_read = u32(0x40000000)
41const pe_image_scn_mem_write = u32(0x80000000)
42
43const pe_import_directory_index = 1
44const pe_exception_directory_index = 3
45const pe_base_reloc_directory_index = 5
46const pe_iat_directory_index = 12
47const pe_runtime_data_alignment = 16
48const pe_wgetenv_buffer_wchars = 32768
49const pe_wgetenv_buffer_bytes = pe_wgetenv_buffer_wchars * 2
50const pe_atexit_callback_capacity = 64
51const pe_run_atexit_symbol = '__v2_pe_run_atexit'
52
53const pe_kernel32_dll = 'kernel32.dll'
54const pe_shell32_dll = 'shell32.dll'
55const pe_ucrtbase_dll = 'ucrtbase.dll'
56const pe_msvcrt_dll = 'msvcrt.dll'
57const pe_kernel32_imports = ['ExitProcess', 'GetCommandLineW', 'GetStdHandle', 'GetConsoleMode',
58 'MultiByteToWideChar', 'WideCharToMultiByte', 'GetCurrentDirectoryW', 'GetEnvironmentVariableW',
59 'ReadConsoleW', 'ReadFile', 'WriteConsoleW', 'WriteFile', 'GetProcessHeap', 'HeapAlloc',
60 'HeapReAlloc', 'HeapFree', 'GetCurrentThreadId', 'GetSystemTimeAsFileTime',
61 'FileTimeToSystemTime', 'SystemTimeToTzSpecificLocalTime', 'QueryPerformanceFrequency',
62 'QueryPerformanceCounter']
63const pe_shell32_imports = ['CommandLineToArgvW']
64const pe_ucrtbase_imports = ['log', 'ldexp', 'sqrt', '_time64', '_localtime64']
65const pe_msvcrt_imports = ['_scprintf', '_snprintf']
66
67struct PeImport {
68 dll string
69 name string
70}
71
72struct PeRuntimeCallPatch {
73 field_off int
74 dll string
75 name string
76}
77
78struct PeRuntimeDataPatch {
79 field_off int
80 data_off u32
81}
82
83struct PeRuntimeText {
84mut:
85 bytes []u8
86 data []u8
87 symbols map[string]u32
88 import_patches []PeRuntimeCallPatch
89 data_patches []PeRuntimeDataPatch
90}
91
92struct PeSection {
93mut:
94 name string
95 data []u8
96 virtual_address u32
97 virtual_size u32
98 raw_pointer u32
99 raw_size u32
100 characteristics u32
101}
102
103struct PeIdata {
104 data []u8
105 import_rva u32
106 import_size u32
107 iat_rva u32
108 iat_size u32
109 iat_rvas map[string]u32
110}
111
112struct PeImportGroup {
113 dll string
114 start int
115mut:
116 len int
117}
118
119struct PeTextBuild {
120 data []u8
121 argc_global_disp_off int
122 argv_global_disp_off int
123}
124
125pub struct PeLinker {
126 coff &CoffObject
127mut:
128 imports []PeImport
129}
130
131pub fn PeLinker.new(coff &CoffObject) &PeLinker {
132 return unsafe {
133 &PeLinker{
134 coff: coff
135 }
136 }
137}
138
139pub fn (mut l PeLinker) write(path string) ! {
140 image := l.image()!
141 os.write_file_array(path, image)!
142 pe_check_written_image(path, image.len)!
143}
144
145pub fn (mut l PeLinker) image() ![]u8 {
146 mut runtime_text := l.build_runtime_text()
147 l.imports = l.required_pe_imports(runtime_text)!
148 l.patch_runtime_import_calls(mut runtime_text)!
149 text_build := l.build_text_section(runtime_text)!
150 mut text := text_build.data.clone()
151 mut data_data := l.coff.data_data.clone()
152 runtime_data_base := if runtime_text.data.len > 0 {
153 base := align_int(data_data.len, pe_runtime_data_alignment)
154 for data_data.len < base {
155 data_data << u8(0)
156 }
157 data_data << runtime_text.data
158 base
159 } else {
160 data_data.len
161 }
162 section_count := 2 + if l.coff.rodata.len > 0 { 1 } else { 0 } +
163 if data_data.len > 0 { 1 } else { 0 }
164 header_size := pe_headers_size(section_count)
165 mut raw_pointer := header_size
166 mut next_rva := u32(pe_section_alignment)
167 mut sections := []PeSection{cap: section_count}
168
169 text_section, next_text_raw := pe_make_section('.text', text, next_rva, raw_pointer,
170 pe_image_scn_cnt_code | pe_image_scn_mem_execute | pe_image_scn_mem_read)
171 raw_pointer = next_text_raw
172 next_rva = pe_next_section_rva(text_section.virtual_address, int(text_section.virtual_size))
173 sections << text_section
174
175 mut rdata_rva := u32(0)
176 if l.coff.rodata.len > 0 {
177 rdata_section, next_rdata_raw := pe_make_section('.rdata', l.coff.rodata, next_rva,
178 raw_pointer, pe_image_scn_cnt_initialized_data | pe_image_scn_mem_read)
179 raw_pointer = next_rdata_raw
180 next_rva = pe_next_section_rva(rdata_section.virtual_address,
181 int(rdata_section.virtual_size))
182 rdata_rva = rdata_section.virtual_address
183 sections << rdata_section
184 }
185
186 mut data_rva := u32(0)
187 if data_data.len > 0 {
188 data_section, next_data_raw := pe_make_section('.data', data_data, next_rva, raw_pointer,
189 pe_image_scn_cnt_initialized_data | pe_image_scn_mem_read | pe_image_scn_mem_write)
190 raw_pointer = next_data_raw
191 next_rva = pe_next_section_rva(data_section.virtual_address, int(data_section.virtual_size))
192 data_rva = data_section.virtual_address
193 sections << data_section
194 }
195
196 idata := l.build_idata(next_rva)
197 idata_section, next_idata_raw := pe_make_section('.idata', idata.data, next_rva, raw_pointer,
198 pe_image_scn_cnt_initialized_data | pe_image_scn_mem_read | pe_image_scn_mem_write)
199 raw_pointer = next_idata_raw
200 sections << idata_section
201
202 import_thunks := l.import_thunk_rvas(text_section.virtual_address, runtime_text.bytes.len)
203 import_iats := idata.iat_rvas.clone()
204 runtime_thunks := runtime_text.symbol_rvas(text_section.virtual_address)
205 l.patch_entry_argv_bootstrap_data_refs(mut text, text_section.virtual_address, data_rva,
206 text_build)!
207 l.apply_text_relocations(mut text, text_section.virtual_address, rdata_rva, data_rva,
208 runtime_text.bytes.len, runtime_thunks, import_thunks, import_iats)!
209 l.apply_runtime_data_patches(mut text, text_section.virtual_address, data_rva, runtime_text,
210 runtime_data_base)!
211 sections[0].data = text
212
213 size_of_image := pe_size_of_image(sections)
214 size_of_code := text_section.raw_size
215 mut size_of_initialized_data := u32(0)
216 for section in sections {
217 if section.characteristics & pe_image_scn_cnt_initialized_data != 0 {
218 size_of_initialized_data += section.raw_size
219 }
220 }
221
222 mut buf := []u8{cap: int(raw_pointer)}
223 write_pe_dos_stub(mut buf)
224 for buf.len < pe_dos_stub_size {
225 buf << 0
226 }
227
228 write_u32_le(mut buf, pe_signature)
229 write_u16_le(mut buf, coff_image_file_machine_amd64)
230 write_u16_le(mut buf, u16(sections.len))
231 write_u32_le(mut buf, 0)
232 write_u32_le(mut buf, 0)
233 write_u32_le(mut buf, 0)
234 write_u16_le(mut buf, pe_size_of_optional_header64)
235 // No .reloc section is emitted yet, so mark the image fixed-base.
236 write_u16_le(mut buf,
237 pe_image_file_relocs_stripped | pe_image_file_executable_image | pe_image_file_large_address_aware)
238
239 l.write_optional_header(mut buf, size_of_code, size_of_initialized_data, size_of_image,
240 header_size, idata, text_section.virtual_address)
241
242 for section in sections {
243 write_pe_section_header(mut buf, section)
244 }
245
246 for buf.len < header_size {
247 buf << 0
248 }
249 for section in sections {
250 if section.raw_size == 0 {
251 continue
252 }
253 for buf.len < int(section.raw_pointer) {
254 buf << 0
255 }
256 buf << section.data
257 for buf.len < int(section.raw_pointer + section.raw_size) {
258 buf << 0
259 }
260 }
261
262 return buf
263}
264
265fn (l PeLinker) build_text_section(runtime_text PeRuntimeText) !PeTextBuild {
266 main_rva := l.defined_symbol_offset('main') or {
267 return error('PE linker requires a defined main symbol')
268 }
269 mut text := []u8{}
270 vinit_offset := l.defined_symbol_offset('_vinit') or { u32(0xffff_ffff) }
271 exit_offset := runtime_text.symbols['exit'] or {
272 return error('PE linker internal error: missing runtime exit helper')
273 }
274 text << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
275 mut get_command_line_field := -1
276 mut command_line_to_argv_field := -1
277 mut argv_failure_exit_field := -1
278 mut argc_global_disp_off := -1
279 mut argv_global_disp_off := -1
280 if l.needs_windows_argv_bootstrap() {
281 text << [u8(0xc7), 0x44, 0x24, 0x20, 0, 0, 0, 0] // mov dword ptr [rsp+32], 0
282 get_command_line_field = pe_emit_call_placeholder(mut text)
283 text << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
284 text << [u8(0x48), 0x8d, 0x54, 0x24, 0x20] // lea rdx, [rsp+32]
285 command_line_to_argv_field = pe_emit_call_placeholder(mut text)
286 text << [u8(0x48), 0x85, 0xc0] // test rax, rax
287 text << [u8(0x75), 0x0a] // jne argv parsed
288 text << [u8(0xb9), 1, 0, 0, 0] // mov ecx, 1
289 argv_failure_exit_field = pe_emit_call_placeholder(mut text)
290 if l.has_main_argv_data_symbol() {
291 argv_global_disp_off = pe_emit_lea_r10_rip_placeholder(mut text)
292 text << [u8(0x49), 0x89, 0x02] // mov [r10], rax
293 }
294 if l.has_main_argc_data_symbol() {
295 argc_global_disp_off = pe_emit_lea_r10_rip_placeholder(mut text)
296 text << [u8(0x8b), 0x4c, 0x24, 0x20] // mov ecx, [rsp+32]
297 text << [u8(0x41), 0x89, 0x0a] // mov [r10], ecx
298 }
299 }
300 if vinit_offset != 0xffff_ffff {
301 pe_emit_call_placeholder(mut text)
302 }
303 pe_emit_call_placeholder(mut text)
304 text << [u8(0x31), 0xc9] // xor ecx, ecx
305 pe_emit_call_placeholder(mut text)
306 text << 0xcc
307
308 entry_stub_len := text.len
309 if entry_stub_len != l.entry_stub_size() {
310 return error('PE linker internal error: entry stub size mismatch (${entry_stub_len} != ${l.entry_stub_size()})')
311 }
312 text << l.coff.text_data
313 text << runtime_text.bytes
314 for _ in l.imports {
315 text << [u8(0xff), 0x25, 0, 0, 0, 0] // jmp qword ptr [rip + disp32]
316 }
317
318 import_offsets := l.import_thunk_offsets(runtime_text.bytes.len)
319 if get_command_line_field >= 0 {
320 get_command_line_thunk_off := import_offsets[pe_kernel32_import_key('GetCommandLineW')] or {
321 return error('PE linker internal error: missing GetCommandLineW import thunk')
322 }
323 command_line_to_argv_thunk_off := import_offsets[pe_shell32_import_key('CommandLineToArgvW')] or {
324 return error('PE linker internal error: missing CommandLineToArgvW import thunk')
325 }
326 pe_patch_rel32(mut text, get_command_line_field, get_command_line_thunk_off, 0)
327 pe_patch_rel32(mut text, command_line_to_argv_field, command_line_to_argv_thunk_off, 0)
328 pe_patch_rel32(mut text, argv_failure_exit_field, exit_offset, 0)
329 }
330
331 mut cursor := 4 + l.windows_argv_bootstrap_size()
332 if vinit_offset != 0xffff_ffff {
333 pe_patch_rel32(mut text, cursor + 1, u32(entry_stub_len) + vinit_offset, 0)
334 cursor += 5
335 }
336 pe_patch_rel32(mut text, cursor + 1, u32(entry_stub_len) + main_rva, 0)
337 cursor += 5
338 cursor += 2
339 pe_patch_rel32(mut text, cursor + 1, exit_offset, 0)
340
341 return PeTextBuild{
342 data: text
343 argc_global_disp_off: argc_global_disp_off
344 argv_global_disp_off: argv_global_disp_off
345 }
346}
347
348fn (l PeLinker) build_runtime_text() PeRuntimeText {
349 runtime_base := u32(l.entry_stub_size() + l.coff.text_data.len)
350 mut rt := PeRuntimeText{
351 symbols: map[string]u32{}
352 }
353 mut run_atexit_offset := -1
354 if l.uses_undefined_symbol('atexit') {
355 count_off := pe_runtime_data_alloc(mut rt, 8, 8)
356 callbacks_off := pe_runtime_data_alloc(mut rt, pe_atexit_callback_capacity * 8, 8)
357 run_atexit_offset = rt.bytes.len
358 rt.symbols[pe_run_atexit_symbol] = runtime_base + u32(run_atexit_offset)
359 pe_emit_runtime_run_atexit(mut rt, count_off, callbacks_off)
360 rt.symbols['atexit'] = runtime_base + u32(rt.bytes.len)
361 pe_emit_runtime_atexit(mut rt, count_off, callbacks_off)
362 }
363 if l.uses_undefined_symbol('_aligned_malloc') {
364 rt.symbols['_aligned_malloc'] = runtime_base + u32(rt.bytes.len)
365 pe_emit_runtime_aligned_malloc(mut rt)
366 }
367 if l.uses_undefined_symbol('_aligned_free') {
368 rt.symbols['_aligned_free'] = runtime_base + u32(rt.bytes.len)
369 pe_emit_runtime_aligned_free(mut rt)
370 }
371 if l.uses_undefined_symbol('_aligned_realloc') {
372 rt.symbols['_aligned_realloc'] = runtime_base + u32(rt.bytes.len)
373 pe_emit_runtime_aligned_realloc(mut rt)
374 }
375 needs_memmove := l.uses_undefined_symbol('memmove') || l.uses_undefined_symbol('memcpy')
376 if needs_memmove {
377 move_off := runtime_base + u32(rt.bytes.len)
378 if l.uses_undefined_symbol('memmove') {
379 rt.symbols['memmove'] = move_off
380 }
381 if l.uses_undefined_symbol('memcpy') {
382 rt.symbols['memcpy'] = move_off
383 }
384 pe_emit_runtime_memmove(mut rt)
385 }
386 if l.uses_undefined_symbol('memset') {
387 rt.symbols['memset'] = runtime_base + u32(rt.bytes.len)
388 pe_emit_runtime_memset(mut rt)
389 }
390 if l.uses_undefined_symbol('strlen') {
391 rt.symbols['strlen'] = runtime_base + u32(rt.bytes.len)
392 pe_emit_runtime_strlen(mut rt)
393 }
394 if l.uses_undefined_symbol('wcslen') {
395 rt.symbols['wcslen'] = runtime_base + u32(rt.bytes.len)
396 pe_emit_runtime_wcslen(mut rt)
397 }
398 if l.uses_undefined_symbol('_wgetcwd') {
399 rt.symbols['_wgetcwd'] = runtime_base + u32(rt.bytes.len)
400 pe_emit_runtime_wgetcwd(mut rt)
401 }
402 if l.uses_undefined_symbol('_wgetenv') {
403 rt.symbols['_wgetenv'] = runtime_base + u32(rt.bytes.len)
404 pe_emit_runtime_wgetenv(mut rt)
405 }
406 if l.uses_undefined_symbol('_errno') {
407 rt.symbols['_errno'] = runtime_base + u32(rt.bytes.len)
408 pe_emit_runtime_errno(mut rt)
409 }
410 if l.uses_undefined_symbol('malloc') {
411 rt.symbols['malloc'] = runtime_base + u32(rt.bytes.len)
412 pe_emit_runtime_malloc(mut rt)
413 }
414 if l.uses_undefined_symbol('calloc') {
415 rt.symbols['calloc'] = runtime_base + u32(rt.bytes.len)
416 pe_emit_runtime_calloc(mut rt)
417 }
418 if l.uses_undefined_symbol('free') {
419 rt.symbols['free'] = runtime_base + u32(rt.bytes.len)
420 pe_emit_runtime_free(mut rt)
421 }
422 if l.uses_undefined_symbol('memcmp') {
423 rt.symbols['memcmp'] = runtime_base + u32(rt.bytes.len)
424 pe_emit_runtime_memcmp(mut rt)
425 }
426 rt.symbols['exit'] = runtime_base + u32(rt.bytes.len)
427 pe_emit_runtime_exit(mut rt, run_atexit_offset)
428 if l.uses_undefined_symbol('builtin__i64__str') {
429 rt.symbols['builtin__i64__str'] = runtime_base + u32(rt.bytes.len)
430 pe_emit_runtime_i64_str(mut rt)
431 }
432 if l.uses_undefined_symbol('builtin__string__+') {
433 rt.symbols['builtin__string__+'] = runtime_base + u32(rt.bytes.len)
434 pe_emit_runtime_string_plus(mut rt)
435 }
436 if l.uses_undefined_symbol('builtin__Array_rune__string') {
437 rt.symbols['builtin__Array_rune__string'] = runtime_base + u32(rt.bytes.len)
438 pe_emit_runtime_array_rune_string(mut rt)
439 }
440 if l.uses_undefined_symbol('builtin__new_array_from_c_array_noscan') {
441 rt.symbols['builtin__new_array_from_c_array_noscan'] = runtime_base + u32(rt.bytes.len)
442 pe_emit_runtime_new_array_from_c_array_noscan(mut rt)
443 }
444 if l.uses_undefined_symbol('builtin____new_array_noscan')
445 || l.uses_undefined_symbol('__new_array_noscan') {
446 start := runtime_base + u32(rt.bytes.len)
447 if l.uses_undefined_symbol('builtin____new_array_noscan') {
448 rt.symbols['builtin____new_array_noscan'] = start
449 }
450 if l.uses_undefined_symbol('__new_array_noscan') {
451 rt.symbols['__new_array_noscan'] = start
452 }
453 pe_emit_runtime_new_array_noscan(mut rt)
454 }
455 return rt
456}
457
458fn (l PeLinker) required_pe_imports(runtime_text PeRuntimeText) ![]PeImport {
459 mut required_kernel32 := map[string]bool{}
460 mut required_shell32 := map[string]bool{}
461 mut required_ucrtbase := map[string]bool{}
462 mut required_msvcrt := map[string]bool{}
463 pe_require_kernel32_import(mut required_kernel32, 'ExitProcess')!
464 if l.needs_windows_argv_bootstrap() {
465 pe_require_kernel32_import(mut required_kernel32, 'GetCommandLineW')!
466 pe_require_shell32_import(mut required_shell32, 'CommandLineToArgvW')!
467 }
468 for patch in runtime_text.import_patches {
469 if patch.dll == pe_kernel32_dll {
470 pe_require_kernel32_import(mut required_kernel32, patch.name)!
471 } else if patch.dll == pe_shell32_dll {
472 pe_require_shell32_import(mut required_shell32, patch.name)!
473 } else {
474 return error('PE linker internal error: unknown import DLL `${patch.dll}` for `${patch.name}`')
475 }
476 }
477 for reloc in l.coff.text_relocs {
478 if reloc.sym_idx < 0 || reloc.sym_idx >= l.coff.symbols.len {
479 continue
480 }
481 sym := l.coff.symbols[reloc.sym_idx]
482 if sym.section != 0 {
483 continue
484 }
485 if _ := runtime_text.symbols[sym.name] {
486 continue
487 }
488 if kernel32_name := pe_kernel32_import_name_for_external_symbol(sym.name) {
489 required_kernel32[kernel32_name] = true
490 } else if pe_shell32_import_is_known(sym.name) {
491 required_shell32[sym.name] = true
492 } else if ucrt_name := pe_ucrtbase_import_name_for_external_symbol(sym.name) {
493 pe_require_ucrtbase_import(mut required_ucrtbase, ucrt_name)!
494 } else if pe_msvcrt_import_is_known(sym.name) {
495 pe_require_msvcrt_import(mut required_msvcrt, sym.name)!
496 }
497 }
498
499 mut imports := []PeImport{cap: required_kernel32.len + required_shell32.len +
500 required_ucrtbase.len + required_msvcrt.len}
501 for name in pe_kernel32_imports {
502 if required_kernel32[name] {
503 imports << PeImport{
504 dll: pe_kernel32_dll
505 name: name
506 }
507 }
508 }
509 for name in pe_shell32_imports {
510 if required_shell32[name] {
511 imports << PeImport{
512 dll: pe_shell32_dll
513 name: name
514 }
515 }
516 }
517 for name in pe_ucrtbase_imports {
518 if required_ucrtbase[name] {
519 imports << PeImport{
520 dll: pe_ucrtbase_dll
521 name: name
522 }
523 }
524 }
525 for name in pe_msvcrt_imports {
526 if required_msvcrt[name] {
527 imports << PeImport{
528 dll: pe_msvcrt_dll
529 name: name
530 }
531 }
532 }
533 return imports
534}
535
536fn (l PeLinker) patch_runtime_import_calls(mut rt PeRuntimeText) ! {
537 runtime_base := u32(l.entry_stub_size() + l.coff.text_data.len)
538 import_offsets := l.import_thunk_offsets(rt.bytes.len)
539 for patch in rt.import_patches {
540 key := pe_import_key(patch.dll, patch.name)
541 target := import_offsets[key] or {
542 return error('PE linker internal error: missing import thunk for `${key}`')
543 }
544 pe_patch_rel32(mut rt.bytes, patch.field_off, target, runtime_base)
545 }
546}
547
548fn pe_import_key(dll string, name string) string {
549 return '${dll}:${name}'
550}
551
552fn pe_kernel32_import_key(name string) string {
553 return pe_import_key(pe_kernel32_dll, name)
554}
555
556fn pe_shell32_import_key(name string) string {
557 return pe_import_key(pe_shell32_dll, name)
558}
559
560fn pe_ucrtbase_import_key(name string) string {
561 return pe_import_key(pe_ucrtbase_dll, name)
562}
563
564fn pe_msvcrt_import_key(name string) string {
565 return pe_import_key(pe_msvcrt_dll, name)
566}
567
568fn pe_require_kernel32_import(mut required map[string]bool, name string) ! {
569 if !pe_kernel32_import_is_known(name) {
570 return error('PE linker internal error: unknown Kernel32 import `${name}`')
571 }
572 required[name] = true
573}
574
575fn pe_kernel32_import_is_known(name string) bool {
576 for known in pe_kernel32_imports {
577 if known == name {
578 return true
579 }
580 }
581 return false
582}
583
584fn pe_kernel32_import_name_for_external_symbol(name string) ?string {
585 return match name {
586 'ReadConsole' {
587 'ReadConsoleW'
588 }
589 else {
590 if pe_kernel32_import_is_known(name) {
591 name
592 } else {
593 none
594 }
595 }
596 }
597}
598
599fn pe_require_ucrtbase_import(mut required map[string]bool, name string) ! {
600 if !pe_ucrtbase_import_is_known(name) {
601 return error('PE linker internal error: unknown UCRT import `${name}`')
602 }
603 required[name] = true
604}
605
606fn pe_ucrtbase_import_is_known(name string) bool {
607 for known in pe_ucrtbase_imports {
608 if known == name {
609 return true
610 }
611 }
612 return false
613}
614
615fn pe_ucrtbase_import_name_for_external_symbol(name string) ?string {
616 return match name {
617 'time' {
618 '_time64'
619 }
620 'localtime' {
621 '_localtime64'
622 }
623 else {
624 if pe_ucrtbase_import_is_known(name) {
625 name
626 } else {
627 none
628 }
629 }
630 }
631}
632
633fn pe_require_msvcrt_import(mut required map[string]bool, name string) ! {
634 if !pe_msvcrt_import_is_known(name) {
635 return error('PE linker internal error: unknown MSVCRT import `${name}`')
636 }
637 required[name] = true
638}
639
640fn pe_msvcrt_import_is_known(name string) bool {
641 for known in pe_msvcrt_imports {
642 if known == name {
643 return true
644 }
645 }
646 return false
647}
648
649fn pe_require_shell32_import(mut required map[string]bool, name string) ! {
650 if !pe_shell32_import_is_known(name) {
651 return error('PE linker internal error: unknown Shell32 import `${name}`')
652 }
653 required[name] = true
654}
655
656fn pe_shell32_import_is_known(name string) bool {
657 for known in pe_shell32_imports {
658 if known == name {
659 return true
660 }
661 }
662 return false
663}
664
665fn (rt PeRuntimeText) symbol_rvas(text_rva u32) map[string]u32 {
666 mut out := map[string]u32{}
667 for name, off in rt.symbols {
668 out[name] = text_rva + off
669 }
670 return out
671}
672
673fn (l PeLinker) build_idata(idata_rva u32) PeIdata {
674 descriptor_size := 20
675 groups := pe_import_groups(l.imports)
676 descriptor_count := groups.len + 1
677 mut cursor := descriptor_size * descriptor_count
678 mut ilt_offsets := []int{cap: groups.len}
679 for group in groups {
680 ilt_offsets << cursor
681 cursor += (group.len + 1) * 8
682 }
683 mut iat_offsets := []int{cap: groups.len}
684 for group in groups {
685 iat_offsets << cursor
686 cursor += (group.len + 1) * 8
687 }
688 hint_name_off := cursor
689
690 mut data := []u8{len: hint_name_off}
691 mut hint_name_rvas := []u32{len: l.imports.len}
692 for i, imp in l.imports {
693 hint_name_rvas[i] = idata_rva + u32(data.len)
694 write_u16_le(mut data, 0)
695 data << imp.name.bytes()
696 data << 0
697 if data.len % 2 != 0 {
698 data << 0
699 }
700 }
701 mut dll_name_rvas := []u32{len: groups.len}
702 for i, group in groups {
703 dll_name_rvas[i] = idata_rva + u32(data.len)
704 data << group.dll.bytes()
705 data << 0
706 if data.len % 2 != 0 {
707 data << 0
708 }
709 }
710
711 mut iat_rvas := map[string]u32{}
712 for group_idx, group in groups {
713 descriptor_off := group_idx * descriptor_size
714 ilt_off := ilt_offsets[group_idx]
715 iat_off := iat_offsets[group_idx]
716 pe_put_u32_le(mut data, descriptor_off, idata_rva + u32(ilt_off))
717 pe_put_u32_le(mut data, descriptor_off + 12, dll_name_rvas[group_idx])
718 pe_put_u32_le(mut data, descriptor_off + 16, idata_rva + u32(iat_off))
719 for local_i in 0 .. group.len {
720 import_idx := group.start + local_i
721 rva := hint_name_rvas[import_idx]
722 pe_put_u64_le(mut data, ilt_off + local_i * 8, u64(rva))
723 pe_put_u64_le(mut data, iat_off + local_i * 8, u64(rva))
724 imp := l.imports[import_idx]
725 iat_rvas[pe_import_key(imp.dll, imp.name)] = idata_rva + u32(iat_off + local_i * 8)
726 }
727 }
728 iat_rva := if groups.len == 0 { idata_rva } else { idata_rva + u32(iat_offsets[0]) }
729 iat_size := if groups.len == 0 {
730 u32(0)
731 } else {
732 last_group := groups[groups.len - 1]
733 last_iat_off := iat_offsets[iat_offsets.len - 1]
734 u32(last_iat_off + (last_group.len + 1) * 8 - iat_offsets[0])
735 }
736
737 return PeIdata{
738 data: data
739 import_rva: idata_rva
740 import_size: u32(descriptor_size * descriptor_count)
741 iat_rva: iat_rva
742 iat_size: iat_size
743 iat_rvas: iat_rvas
744 }
745}
746
747fn pe_import_groups(imports []PeImport) []PeImportGroup {
748 mut groups := []PeImportGroup{}
749 for i, imp in imports {
750 if groups.len == 0 || groups[groups.len - 1].dll != imp.dll {
751 groups << PeImportGroup{
752 dll: imp.dll
753 start: i
754 len: 1
755 }
756 } else {
757 groups[groups.len - 1].len++
758 }
759 }
760 return groups
761}
762
763fn (l PeLinker) apply_text_relocations(mut text []u8,
764 text_rva u32,
765 rdata_rva u32,
766 data_rva u32,
767 runtime_text_size int,
768 runtime_thunks map[string]u32,
769 import_thunks map[string]u32,
770 import_iats map[string]u32) ! {
771 entry_stub_len := l.entry_stub_size()
772 for reloc in l.coff.text_relocs {
773 if reloc.type_ != coff_image_rel_amd64_rel32 {
774 return error('PE linker does not support COFF relocation type 0x${reloc.type_:04x}')
775 }
776 if reloc.sym_idx < 0 || reloc.sym_idx >= l.coff.symbols.len {
777 return error('PE linker relocation references invalid symbol index ${reloc.sym_idx}')
778 }
779 sym := l.coff.symbols[reloc.sym_idx]
780 target_rva := match sym.section {
781 1 {
782 text_rva + u32(entry_stub_len) + sym.value
783 }
784 2 {
785 if rdata_rva == 0 {
786 return error('PE linker relocation references .rdata, but no .rdata section was emitted')
787 }
788 rdata_rva + sym.value
789 }
790 3 {
791 if data_rva == 0 || l.coff.data_data.len == 0 {
792 return error('PE linker relocation references .data, but no .data section was emitted')
793 }
794 data_rva + sym.value
795 }
796 0 {
797 if runtime_rva := runtime_thunks[sym.name] {
798 runtime_rva
799 } else {
800 import_key := l.import_key_for_external_symbol(sym.name) or {
801 return error(l.unresolved_external_symbol_message(sym.name, reloc))
802 }
803 import_thunks[import_key] or {
804 return error(l.unresolved_external_symbol_message(sym.name, reloc))
805 }
806 }
807 }
808 else {
809 return error('PE linker cannot resolve symbol `${sym.name}` in COFF section ${sym.section}')
810 }
811 }
812
813 field_off := entry_stub_len + int(reloc.offset)
814 old_disp := pe_read_i32_le(text, field_off)
815 field_rva := text_rva + u32(field_off)
816 new_disp := old_disp + i32(int(target_rva) - (int(field_rva) + 4))
817 pe_put_u32_le(mut text, field_off, u32(new_disp))
818 }
819
820 thunk_base := l.import_thunk_base(runtime_text_size)
821 for i, imp in l.imports {
822 thunk_off := thunk_base + i * 6
823 import_key := pe_import_key(imp.dll, imp.name)
824 iat_entry_rva := import_iats[import_key] or {
825 return error('PE linker internal error: missing IAT entry for `${import_key}`')
826 }
827 thunk_rva := text_rva + u32(thunk_off)
828 disp := i32(int(iat_entry_rva) - (int(thunk_rva) + 6))
829 pe_put_u32_le(mut text, thunk_off + 2, u32(disp))
830 }
831}
832
833fn (l PeLinker) apply_runtime_data_patches(mut text []u8,
834 text_rva u32,
835 data_rva u32,
836 runtime_text PeRuntimeText,
837 runtime_data_base int) ! {
838 if runtime_text.data_patches.len == 0 {
839 return
840 }
841 if data_rva == 0 {
842 return error('PE linker runtime data patch requires a .data section')
843 }
844 runtime_base := l.entry_stub_size() + l.coff.text_data.len
845 for patch in runtime_text.data_patches {
846 field_off := runtime_base + patch.field_off
847 target_rva := data_rva + u32(runtime_data_base) + patch.data_off
848 pe_patch_rel32(mut text, field_off, target_rva, text_rva)
849 }
850}
851
852fn (l PeLinker) unresolved_external_symbol_message(name string, reloc CoffRelocation) string {
853 context := l.text_relocation_context(reloc)
854 return x64_unresolved_external_symbol_message(.coff, name, context)
855}
856
857fn (l PeLinker) text_relocation_context(reloc CoffRelocation) string {
858 mut has_symbol := false
859 mut nearest_name := ''
860 mut nearest_offset := u32(0)
861 for sym in l.coff.symbols {
862 if sym.section != 1 || sym.name.len == 0 || sym.value > reloc.offset {
863 continue
864 }
865 if !has_symbol || sym.value >= nearest_offset {
866 has_symbol = true
867 nearest_name = sym.name
868 nearest_offset = sym.value
869 }
870 }
871 mut context := 'referenced from .text relocation offset 0x${reloc.offset:08x}'
872 if has_symbol {
873 context += ' near `${nearest_name}`+0x${reloc.offset - nearest_offset:08x}'
874 }
875 return context
876}
877
878fn (l PeLinker) import_thunk_base(runtime_text_size int) int {
879 return l.entry_stub_size() + l.coff.text_data.len + runtime_text_size
880}
881
882fn (l PeLinker) import_thunk_offsets(runtime_text_size int) map[string]u32 {
883 thunk_base := l.import_thunk_base(runtime_text_size)
884 mut out := map[string]u32{}
885 for i, imp in l.imports {
886 out[pe_import_key(imp.dll, imp.name)] = u32(thunk_base + i * 6)
887 }
888 return out
889}
890
891fn (l PeLinker) import_thunk_rvas(text_rva u32, runtime_text_size int) map[string]u32 {
892 offsets := l.import_thunk_offsets(runtime_text_size)
893 mut out := map[string]u32{}
894 for name, off in offsets {
895 out[name] = text_rva + off
896 }
897 return out
898}
899
900fn (l PeLinker) import_key_for_external_symbol(name string) ?string {
901 if kernel32_name := pe_kernel32_import_name_for_external_symbol(name) {
902 return pe_kernel32_import_key(kernel32_name)
903 }
904 if pe_shell32_import_is_known(name) {
905 return pe_shell32_import_key(name)
906 }
907 if ucrt_name := pe_ucrtbase_import_name_for_external_symbol(name) {
908 return pe_ucrtbase_import_key(ucrt_name)
909 }
910 if pe_msvcrt_import_is_known(name) {
911 return pe_msvcrt_import_key(name)
912 }
913 return none
914}
915
916fn (l PeLinker) entry_stub_size() int {
917 vinit := l.defined_symbol_offset('_vinit') or { u32(0xffff_ffff) }
918 base_size := if vinit == 0xffff_ffff { 17 } else { 22 }
919 return base_size + l.windows_argv_bootstrap_size()
920}
921
922fn (l PeLinker) windows_argv_bootstrap_size() int {
923 if !l.needs_windows_argv_bootstrap() {
924 return 0
925 }
926 mut size := 8 + 5 + 3 + 5 + 5 + 15 // zero argc, argv calls, null check, ExitProcess(1)
927 if l.has_main_argv_data_symbol() {
928 size += 7 + 3 // lea g_main_argv; mov [g_main_argv], rax
929 }
930 if l.has_main_argc_data_symbol() {
931 size += 7 + 4 + 3 // lea g_main_argc; load local argc; mov [g_main_argc], ecx
932 }
933 return size
934}
935
936fn (l PeLinker) defined_symbol_offset(name string) ?u32 {
937 for sym in l.coff.symbols {
938 if sym.name == name && sym.section == 1 {
939 return sym.value
940 }
941 }
942 return none
943}
944
945fn (l PeLinker) uses_undefined_symbol(name string) bool {
946 for reloc in l.coff.text_relocs {
947 if reloc.sym_idx < 0 || reloc.sym_idx >= l.coff.symbols.len {
948 continue
949 }
950 sym := l.coff.symbols[reloc.sym_idx]
951 if sym.section == 0 && sym.name == name {
952 return true
953 }
954 }
955 return false
956}
957
958fn (l PeLinker) needs_windows_argv_bootstrap() bool {
959 for reloc in l.coff.text_relocs {
960 if reloc.sym_idx < 0 || reloc.sym_idx >= l.coff.symbols.len {
961 continue
962 }
963 sym := l.coff.symbols[reloc.sym_idx]
964 if sym.section == 3
965 && (x64_main_argc_global_name(sym.name) || x64_main_argv_global_name(sym.name)) {
966 return true
967 }
968 }
969 return false
970}
971
972fn (l PeLinker) has_main_argc_data_symbol() bool {
973 if _ := l.main_argc_data_offset() {
974 return true
975 }
976 return false
977}
978
979fn (l PeLinker) has_main_argv_data_symbol() bool {
980 if _ := l.main_argv_data_offset() {
981 return true
982 }
983 return false
984}
985
986fn (l PeLinker) main_argc_data_offset() ?u32 {
987 for sym in l.coff.symbols {
988 if sym.section == 3 && x64_main_argc_global_name(sym.name) {
989 return sym.value
990 }
991 }
992 return none
993}
994
995fn (l PeLinker) main_argv_data_offset() ?u32 {
996 for sym in l.coff.symbols {
997 if sym.section == 3 && x64_main_argv_global_name(sym.name) {
998 return sym.value
999 }
1000 }
1001 return none
1002}
1003
1004fn (l PeLinker) patch_entry_argv_bootstrap_data_refs(mut text []u8, text_rva u32, data_rva u32, text_build PeTextBuild) ! {
1005 if text_build.argc_global_disp_off < 0 && text_build.argv_global_disp_off < 0 {
1006 return
1007 }
1008 if data_rva == 0 {
1009 return error('PE linker Windows argv bootstrap requires a .data section for g_main_argc/g_main_argv')
1010 }
1011 if text_build.argc_global_disp_off >= 0 {
1012 argc_off := l.main_argc_data_offset() or {
1013 return error('PE linker Windows argv bootstrap requires g_main_argc data symbol')
1014 }
1015 pe_patch_rel32(mut text, text_build.argc_global_disp_off, data_rva + argc_off, text_rva)
1016 }
1017 if text_build.argv_global_disp_off >= 0 {
1018 argv_off := l.main_argv_data_offset() or {
1019 return error('PE linker Windows argv bootstrap requires g_main_argv data symbol')
1020 }
1021 pe_patch_rel32(mut text, text_build.argv_global_disp_off, data_rva + argv_off, text_rva)
1022 }
1023}
1024
1025fn (mut l PeLinker) write_optional_header(mut buf []u8, size_of_code u32, size_of_initialized_data u32, size_of_image u32, size_of_headers int, idata PeIdata, entry_rva u32) {
1026 write_u16_le(mut buf, pe_optional_header64_magic)
1027 buf << pe_linker_major_version
1028 buf << pe_linker_minor_version
1029 write_u32_le(mut buf, size_of_code)
1030 write_u32_le(mut buf, size_of_initialized_data)
1031 write_u32_le(mut buf, 0)
1032 write_u32_le(mut buf, entry_rva)
1033 write_u32_le(mut buf, entry_rva)
1034 write_u64_le(mut buf, pe_image_base)
1035 write_u32_le(mut buf, pe_section_alignment)
1036 write_u32_le(mut buf, pe_file_alignment)
1037 write_u16_le(mut buf, pe_major_operating_system_version)
1038 write_u16_le(mut buf, pe_minor_operating_system_version)
1039 write_u16_le(mut buf, 0)
1040 write_u16_le(mut buf, 0)
1041 write_u16_le(mut buf, pe_major_subsystem_version)
1042 write_u16_le(mut buf, pe_minor_subsystem_version)
1043 write_u32_le(mut buf, 0)
1044 write_u32_le(mut buf, size_of_image)
1045 write_u32_le(mut buf, u32(size_of_headers))
1046 write_u32_le(mut buf, 0)
1047 write_u16_le(mut buf, pe_image_subsystem_windows_cui)
1048 write_u16_le(mut buf, pe_dll_characteristics_nx_compat)
1049 write_u64_le(mut buf, pe_size_of_stack_reserve)
1050 write_u64_le(mut buf, pe_size_of_stack_commit)
1051 write_u64_le(mut buf, pe_size_of_heap_reserve)
1052 write_u64_le(mut buf, pe_size_of_heap_commit)
1053 write_u32_le(mut buf, 0)
1054 write_u32_le(mut buf, pe_number_of_rva_and_sizes)
1055
1056 for i in 0 .. pe_number_of_rva_and_sizes {
1057 if i == pe_import_directory_index {
1058 write_u32_le(mut buf, idata.import_rva)
1059 write_u32_le(mut buf, idata.import_size)
1060 } else if i == pe_exception_directory_index || i == pe_base_reloc_directory_index {
1061 // Unwind and base relocation directories are not emitted yet.
1062 write_u32_le(mut buf, 0)
1063 write_u32_le(mut buf, 0)
1064 } else if i == pe_iat_directory_index {
1065 write_u32_le(mut buf, idata.iat_rva)
1066 write_u32_le(mut buf, idata.iat_size)
1067 } else {
1068 write_u32_le(mut buf, 0)
1069 write_u32_le(mut buf, 0)
1070 }
1071 }
1072}
1073
1074fn write_pe_dos_stub(mut buf []u8) {
1075 buf << [u8(`M`), `Z`]
1076 for buf.len < 0x3c {
1077 buf << 0
1078 }
1079 write_u32_le(mut buf, pe_dos_stub_size)
1080}
1081
1082fn write_pe_section_header(mut buf []u8, section PeSection) {
1083 write_fixed_string(mut buf, section.name, 8)
1084 write_u32_le(mut buf, section.virtual_size)
1085 write_u32_le(mut buf, section.virtual_address)
1086 write_u32_le(mut buf, section.raw_size)
1087 write_u32_le(mut buf, section.raw_pointer)
1088 write_u32_le(mut buf, 0)
1089 write_u32_le(mut buf, 0)
1090 write_u16_le(mut buf, 0)
1091 write_u16_le(mut buf, 0)
1092 write_u32_le(mut buf, section.characteristics)
1093}
1094
1095fn pe_make_section(name string, data []u8, virtual_address u32, raw_pointer int, characteristics u32) (PeSection, int) {
1096 raw_size := if data.len == 0 { 0 } else { align_int(data.len, pe_file_alignment) }
1097 raw_ptr := if raw_size == 0 { 0 } else { raw_pointer }
1098 mut next_raw_pointer := raw_pointer
1099 if raw_size > 0 {
1100 next_raw_pointer += raw_size
1101 }
1102 return PeSection{
1103 name: name
1104 data: data.clone()
1105 virtual_address: virtual_address
1106 virtual_size: u32(data.len)
1107 raw_pointer: u32(raw_ptr)
1108 raw_size: u32(raw_size)
1109 characteristics: characteristics
1110 }, next_raw_pointer
1111}
1112
1113fn pe_headers_size(section_count int) int {
1114 return align_int(pe_dos_stub_size + 4 + 20 + int(pe_size_of_optional_header64) +
1115 section_count * 40, pe_file_alignment)
1116}
1117
1118fn pe_size_of_image(sections []PeSection) u32 {
1119 mut end := 0
1120 for section in sections {
1121 virtual_size := if section.virtual_size == 0 { 1 } else { int(section.virtual_size) }
1122 section_end := int(section.virtual_address) + align_int(virtual_size, pe_section_alignment)
1123 if section_end > end {
1124 end = section_end
1125 }
1126 }
1127 return u32(align_int(end, pe_section_alignment))
1128}
1129
1130fn pe_next_section_rva(rva u32, size int) u32 {
1131 virtual_size := if size == 0 { 1 } else { size }
1132 return u32(align_int(int(rva) + virtual_size, pe_section_alignment))
1133}
1134
1135fn pe_check_written_image(path string, expected_size int) ! {
1136 if !os.is_file(path) {
1137 return error('PE linker write reported success, but `${path}` was not created as a file')
1138 }
1139 size := os.file_size(path)
1140 if size != u64(expected_size) {
1141 return error('PE linker write reported success, but `${path}` has size ${size} bytes (expected ${expected_size} bytes)')
1142 }
1143}
1144
1145fn pe_emit_call_placeholder(mut text []u8) int {
1146 text << u8(0xe8)
1147 field_off := text.len
1148 text << [u8(0), 0, 0, 0]
1149 return field_off
1150}
1151
1152fn pe_emit_lea_r10_rip_placeholder(mut text []u8) int {
1153 text << [u8(0x4c), 0x8d, 0x15] // lea r10, [rip + disp32]
1154 field_off := text.len
1155 text << [u8(0), 0, 0, 0]
1156 return field_off
1157}
1158
1159fn pe_emit_runtime_call_import(mut rt PeRuntimeText, import_name string) {
1160 rt.bytes << u8(0xe8)
1161 rt.import_patches << PeRuntimeCallPatch{
1162 field_off: rt.bytes.len
1163 dll: pe_kernel32_dll
1164 name: import_name
1165 }
1166 rt.bytes << [u8(0), 0, 0, 0]
1167}
1168
1169fn pe_runtime_data_alloc(mut rt PeRuntimeText, size int, align int) u32 {
1170 aligned := align_int(rt.data.len, align)
1171 for rt.data.len < aligned {
1172 rt.data << u8(0)
1173 }
1174 off := rt.data.len
1175 for _ in 0 .. size {
1176 rt.data << u8(0)
1177 }
1178 return u32(off)
1179}
1180
1181fn pe_emit_runtime_lea_rax_data(mut rt PeRuntimeText, data_off u32) {
1182 rt.bytes << [u8(0x48), 0x8d, 0x05] // lea rax, [rip + disp32]
1183 rt.data_patches << PeRuntimeDataPatch{
1184 field_off: rt.bytes.len
1185 data_off: data_off
1186 }
1187 rt.bytes << [u8(0), 0, 0, 0]
1188}
1189
1190fn pe_emit_runtime_lea_rdx_data(mut rt PeRuntimeText, data_off u32) {
1191 rt.bytes << [u8(0x48), 0x8d, 0x15] // lea rdx, [rip + disp32]
1192 rt.data_patches << PeRuntimeDataPatch{
1193 field_off: rt.bytes.len
1194 data_off: data_off
1195 }
1196 rt.bytes << [u8(0), 0, 0, 0]
1197}
1198
1199fn pe_emit_runtime_lea_r8_data(mut rt PeRuntimeText, data_off u32) {
1200 rt.bytes << [u8(0x4c), 0x8d, 0x05] // lea r8, [rip + disp32]
1201 rt.data_patches << PeRuntimeDataPatch{
1202 field_off: rt.bytes.len
1203 data_off: data_off
1204 }
1205 rt.bytes << [u8(0), 0, 0, 0]
1206}
1207
1208fn pe_emit_jcc8(mut data []u8, opcode u8) int {
1209 data << opcode
1210 field_off := data.len
1211 data << u8(0)
1212 return field_off
1213}
1214
1215fn pe_emit_jcc32(mut data []u8, opcode u8) int {
1216 data << [u8(0x0f), opcode]
1217 field_off := data.len
1218 data << [u8(0), 0, 0, 0]
1219 return field_off
1220}
1221
1222fn pe_emit_jmp32(mut data []u8) int {
1223 data << u8(0xe9)
1224 field_off := data.len
1225 data << [u8(0), 0, 0, 0]
1226 return field_off
1227}
1228
1229fn pe_patch_rel32(mut text []u8, field_off int, target_off u32, text_rva u32) {
1230 field_rva := text_rva + u32(field_off)
1231 disp := i32(int(target_off) - (int(field_rva) + 4))
1232 pe_put_u32_le(mut text, field_off, u32(disp))
1233}
1234
1235fn pe_patch_rel32_local(mut data []u8, field_off int, target_off int) {
1236 pe_patch_rel32(mut data, field_off, u32(target_off), 0)
1237}
1238
1239fn pe_patch_rel8(mut data []u8, field_off int, target_off int) {
1240 disp := target_off - (field_off + 1)
1241 data[field_off] = u8(disp & 0xff)
1242}
1243
1244fn pe_emit_runtime_aligned_malloc(mut rt PeRuntimeText) {
1245 rt.bytes << [u8(0x48), 0x83, 0xec, 0x38] // sub rsp, 56
1246 rt.bytes << [u8(0x48), 0x83, 0xfa, 0x10] // cmp rdx, 16
1247 align_check := pe_emit_jcc8(mut rt.bytes, 0x77) // ja
1248 rt.bytes << [u8(0xba), 0x10, 0, 0, 0] // mov edx, 16
1249 align_ready_jump := pe_emit_jcc8(mut rt.bytes, 0xeb) // jmp
1250 align_check_target := rt.bytes.len
1251 rt.bytes << [u8(0x49), 0x89, 0xd2] // mov r10, rdx
1252 rt.bytes << [u8(0x49), 0xff, 0xca] // dec r10
1253 rt.bytes << [u8(0x4c), 0x85, 0xd2] // test rdx, r10
1254 invalid_align := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1255 align_ready := rt.bytes.len
1256 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1257 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1258 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1259 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1260 no_heap := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1261 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1262 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1263 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x20] // mov r8, [rsp+32]
1264 rt.bytes << [u8(0x4c), 0x8b, 0x4c, 0x24, 0x28] // mov r9, [rsp+40]
1265 rt.bytes << [u8(0x4d), 0x01, 0xc8] // add r8, r9
1266 size_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1267 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x08] // add r8, 8
1268 padding_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1269 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1270 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1271 alloc_failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1272 rt.bytes << [u8(0x4c), 0x8b, 0x54, 0x24, 0x28] // mov r10, [rsp+40]
1273 rt.bytes << [u8(0x49), 0x89, 0xc3] // mov r11, rax
1274 rt.bytes << [u8(0x49), 0x83, 0xc3, 0x08] // add r11, 8
1275 rt.bytes << [u8(0x4d), 0x01, 0xd3] // add r11, r10
1276 rt.bytes << [u8(0x49), 0xff, 0xcb] // dec r11
1277 rt.bytes << [u8(0x4c), 0x89, 0xd1] // mov rcx, r10
1278 rt.bytes << [u8(0x48), 0xf7, 0xd9] // neg rcx
1279 rt.bytes << [u8(0x49), 0x21, 0xcb] // and r11, rcx
1280 rt.bytes << [u8(0x49), 0x89, 0x43, 0xf8] // mov [r11-8], rax
1281 rt.bytes << [u8(0x4c), 0x89, 0xd8] // mov rax, r11
1282 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1283 rt.bytes << u8(0xc3) // ret
1284 fail := rt.bytes.len
1285 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1286 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1287 rt.bytes << u8(0xc3) // ret
1288 pe_patch_rel8(mut rt.bytes, align_check, align_check_target)
1289 pe_patch_rel8(mut rt.bytes, align_ready_jump, align_ready)
1290 pe_patch_rel8(mut rt.bytes, invalid_align, fail)
1291 pe_patch_rel8(mut rt.bytes, no_heap, fail)
1292 pe_patch_rel8(mut rt.bytes, size_overflow, fail)
1293 pe_patch_rel8(mut rt.bytes, padding_overflow, fail)
1294 pe_patch_rel8(mut rt.bytes, alloc_failed, fail)
1295}
1296
1297fn pe_emit_runtime_aligned_free(mut rt PeRuntimeText) {
1298 rt.bytes << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
1299 null_ptr := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1300 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1301 rt.bytes << [u8(0x48), 0x8b, 0x41, 0xf8] // mov rax, [rcx-8]
1302 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x20] // mov [rsp+32], rax
1303 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1304 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1305 no_heap := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1306 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1307 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1308 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x20] // mov r8, [rsp+32]
1309 pe_emit_runtime_call_import(mut rt, 'HeapFree')
1310 cleanup := rt.bytes.len
1311 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1312 done := rt.bytes.len
1313 rt.bytes << u8(0xc3) // ret
1314 pe_patch_rel8(mut rt.bytes, null_ptr, done)
1315 pe_patch_rel8(mut rt.bytes, no_heap, cleanup)
1316}
1317
1318fn pe_emit_runtime_aligned_realloc(mut rt PeRuntimeText) {
1319 rt.bytes << [u8(0x49), 0x83, 0xf8, 0x10] // cmp r8, 16
1320 supported_align := pe_emit_jcc8(mut rt.bytes, 0x76) // jbe
1321 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1322 rt.bytes << u8(0xc3) // ret
1323 supported_align_target := rt.bytes.len
1324 rt.bytes << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
1325 null_ptr := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1326 rt.bytes << [u8(0x48), 0x83, 0xec, 0x38] // sub rsp, 56
1327 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x20] // mov [rsp+32], rdx
1328 rt.bytes << [u8(0x4c), 0x8b, 0x51, 0xf8] // mov r10, [rcx-8]
1329 rt.bytes << [u8(0x4c), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], r10
1330 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1331 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1332 no_heap := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1333 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1334 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1335 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x28] // mov r8, [rsp+40]
1336 rt.bytes << [u8(0x4c), 0x8b, 0x4c, 0x24, 0x20] // mov r9, [rsp+32]
1337 rt.bytes << [u8(0x49), 0x83, 0xc1, 0x18] // add r9, 24
1338 size_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1339 pe_emit_runtime_call_import(mut rt, 'HeapReAlloc')
1340 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1341 realloc_failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1342 rt.bytes << [u8(0x49), 0x89, 0xc2] // mov r10, rax
1343 rt.bytes << [u8(0x49), 0x83, 0xc2, 0x10] // add r10, 16
1344 rt.bytes << [u8(0x49), 0x89, 0x42, 0xf8] // mov [r10-8], rax
1345 rt.bytes << [u8(0x4c), 0x89, 0xd0] // mov rax, r10
1346 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1347 rt.bytes << u8(0xc3) // ret
1348 fail_with_frame := rt.bytes.len
1349 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1350 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1351 rt.bytes << u8(0xc3) // ret
1352 null_alloc := rt.bytes.len
1353 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1354 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x20] // mov [rsp+32], rdx
1355 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1356 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1357 no_heap_null := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1358 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1359 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1360 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x20] // mov r8, [rsp+32]
1361 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x18] // add r8, 24
1362 null_size_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1363 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1364 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1365 alloc_failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1366 rt.bytes << [u8(0x49), 0x89, 0xc2] // mov r10, rax
1367 rt.bytes << [u8(0x49), 0x83, 0xc2, 0x10] // add r10, 16
1368 rt.bytes << [u8(0x49), 0x89, 0x42, 0xf8] // mov [r10-8], rax
1369 rt.bytes << [u8(0x4c), 0x89, 0xd0] // mov rax, r10
1370 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1371 rt.bytes << u8(0xc3) // ret
1372 fail_with_null_frame := rt.bytes.len
1373 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1374 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1375 rt.bytes << u8(0xc3) // ret
1376 pe_patch_rel8(mut rt.bytes, supported_align, supported_align_target)
1377 pe_patch_rel8(mut rt.bytes, null_ptr, null_alloc)
1378 pe_patch_rel8(mut rt.bytes, no_heap, fail_with_frame)
1379 pe_patch_rel8(mut rt.bytes, size_overflow, fail_with_frame)
1380 pe_patch_rel8(mut rt.bytes, realloc_failed, fail_with_frame)
1381 pe_patch_rel8(mut rt.bytes, no_heap_null, fail_with_null_frame)
1382 pe_patch_rel8(mut rt.bytes, null_size_overflow, fail_with_null_frame)
1383 pe_patch_rel8(mut rt.bytes, alloc_failed, fail_with_null_frame)
1384}
1385
1386fn pe_emit_runtime_align_heap_allocated_data(mut rt PeRuntimeText) {
1387 rt.bytes << [u8(0x49), 0x89, 0xc3] // mov r11, rax
1388 rt.bytes << [u8(0x49), 0x83, 0xc3, 0x17] // add r11, 23
1389 rt.bytes << [u8(0x49), 0x83, 0xe3, 0xf0] // and r11, -16
1390 rt.bytes << [u8(0x49), 0x89, 0x43, 0xf8] // mov [r11-8], rax
1391}
1392
1393fn pe_emit_runtime_memmove(mut rt PeRuntimeText) {
1394 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
1395 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1396 zero_len := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1397 rt.bytes << [u8(0x48), 0x39, 0xd1] // cmp rcx, rdx
1398 dest_before_src := pe_emit_jcc8(mut rt.bytes, 0x76) // jbe
1399 rt.bytes << [u8(0x4e), 0x8d, 0x0c, 0x02] // lea r9, [rdx+r8]
1400 rt.bytes << [u8(0x4c), 0x39, 0xc9] // cmp rcx, r9
1401 no_overlap := pe_emit_jcc8(mut rt.bytes, 0x73) // jae
1402 rt.bytes << [u8(0x4e), 0x8d, 0x54, 0x01, 0xff] // lea r10, [rcx+r8-1]
1403 rt.bytes << [u8(0x4e), 0x8d, 0x5c, 0x02, 0xff] // lea r11, [rdx+r8-1]
1404 backward_loop := rt.bytes.len
1405 rt.bytes << [u8(0x45), 0x8a, 0x0b] // mov r9b, [r11]
1406 rt.bytes << [u8(0x45), 0x88, 0x0a] // mov [r10], r9b
1407 rt.bytes << [u8(0x49), 0xff, 0xca] // dec r10
1408 rt.bytes << [u8(0x49), 0xff, 0xcb] // dec r11
1409 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1410 backward_more := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1411 rt.bytes << u8(0xc3) // ret
1412 forward := rt.bytes.len
1413 rt.bytes << [u8(0x49), 0x89, 0xca] // mov r10, rcx
1414 rt.bytes << [u8(0x49), 0x89, 0xd3] // mov r11, rdx
1415 forward_loop := rt.bytes.len
1416 rt.bytes << [u8(0x45), 0x8a, 0x0b] // mov r9b, [r11]
1417 rt.bytes << [u8(0x45), 0x88, 0x0a] // mov [r10], r9b
1418 rt.bytes << [u8(0x49), 0xff, 0xc2] // inc r10
1419 rt.bytes << [u8(0x49), 0xff, 0xc3] // inc r11
1420 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1421 forward_more := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1422 done := rt.bytes.len
1423 rt.bytes << u8(0xc3) // ret
1424 pe_patch_rel8(mut rt.bytes, zero_len, done)
1425 pe_patch_rel8(mut rt.bytes, dest_before_src, forward)
1426 pe_patch_rel8(mut rt.bytes, no_overlap, forward)
1427 pe_patch_rel8(mut rt.bytes, backward_more, backward_loop)
1428 pe_patch_rel8(mut rt.bytes, forward_more, forward_loop)
1429}
1430
1431fn pe_emit_runtime_memset(mut rt PeRuntimeText) {
1432 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
1433 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1434 zero_len := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1435 rt.bytes << [u8(0x49), 0x89, 0xca] // mov r10, rcx
1436 loop_start := rt.bytes.len
1437 rt.bytes << [u8(0x41), 0x88, 0x12] // mov [r10], dl
1438 rt.bytes << [u8(0x49), 0xff, 0xc2] // inc r10
1439 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1440 more := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1441 done := rt.bytes.len
1442 rt.bytes << u8(0xc3) // ret
1443 pe_patch_rel8(mut rt.bytes, zero_len, done)
1444 pe_patch_rel8(mut rt.bytes, more, loop_start)
1445}
1446
1447fn pe_emit_runtime_strlen(mut rt PeRuntimeText) {
1448 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
1449 loop_start := rt.bytes.len
1450 rt.bytes << [u8(0x80), 0x38, 0x00] // cmp byte ptr [rax], 0
1451 done := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1452 rt.bytes << [u8(0x48), 0xff, 0xc0] // inc rax
1453 more := pe_emit_jcc8(mut rt.bytes, 0xeb) // jmp
1454 done_target := rt.bytes.len
1455 rt.bytes << [u8(0x48), 0x29, 0xc8] // sub rax, rcx
1456 rt.bytes << u8(0xc3) // ret
1457 pe_patch_rel8(mut rt.bytes, done, done_target)
1458 pe_patch_rel8(mut rt.bytes, more, loop_start)
1459}
1460
1461fn pe_emit_runtime_wcslen(mut rt PeRuntimeText) {
1462 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
1463 loop_start := rt.bytes.len
1464 rt.bytes << [u8(0x66), 0x83, 0x38, 0x00] // cmp word ptr [rax], 0
1465 done := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1466 rt.bytes << [u8(0x48), 0x83, 0xc0, 0x02] // add rax, 2
1467 more := pe_emit_jcc8(mut rt.bytes, 0xeb) // jmp
1468 done_target := rt.bytes.len
1469 rt.bytes << [u8(0x48), 0x29, 0xc8] // sub rax, rcx
1470 rt.bytes << [u8(0x48), 0xd1, 0xe8] // shr rax, 1
1471 rt.bytes << u8(0xc3) // ret
1472 pe_patch_rel8(mut rt.bytes, done, done_target)
1473 pe_patch_rel8(mut rt.bytes, more, loop_start)
1474}
1475
1476fn pe_emit_runtime_wgetcwd(mut rt PeRuntimeText) {
1477 rt.bytes << [u8(0x48), 0x83, 0xec, 0x38] // sub rsp, 56
1478 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1479 rt.bytes << [u8(0x89), 0x54, 0x24, 0x28] // mov [rsp+40], edx
1480 rt.bytes << [u8(0x89), 0xd1] // mov ecx, edx
1481 rt.bytes << [u8(0x48), 0x8b, 0x54, 0x24, 0x20] // mov rdx, [rsp+32]
1482 pe_emit_runtime_call_import(mut rt, 'GetCurrentDirectoryW')
1483 rt.bytes << [u8(0x85), 0xc0] // test eax, eax
1484 failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1485 rt.bytes << [u8(0x3b), 0x44, 0x24, 0x28] // cmp eax, [rsp+40]
1486 too_small := pe_emit_jcc8(mut rt.bytes, 0x73) // jae
1487 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
1488 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1489 rt.bytes << u8(0xc3) // ret
1490 fail_target := rt.bytes.len
1491 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1492 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x38] // add rsp, 56
1493 rt.bytes << u8(0xc3) // ret
1494 pe_patch_rel8(mut rt.bytes, failed, fail_target)
1495 pe_patch_rel8(mut rt.bytes, too_small, fail_target)
1496}
1497
1498fn pe_emit_runtime_wgetenv(mut rt PeRuntimeText) {
1499 buffer_off := pe_runtime_data_alloc(mut rt, pe_wgetenv_buffer_bytes, pe_runtime_data_alignment)
1500 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1501 rt.bytes << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
1502 null_name := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1503 pe_emit_runtime_lea_rdx_data(mut rt, buffer_off)
1504 rt.bytes << [u8(0x41), 0xb8] // mov r8d, pe_wgetenv_buffer_wchars
1505 write_u32_le(mut rt.bytes, u32(pe_wgetenv_buffer_wchars))
1506 pe_emit_runtime_call_import(mut rt, 'GetEnvironmentVariableW')
1507 rt.bytes << [u8(0x85), 0xc0] // test eax, eax
1508 missing := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1509 rt.bytes << u8(0x3d) // cmp eax, pe_wgetenv_buffer_wchars
1510 write_u32_le(mut rt.bytes, u32(pe_wgetenv_buffer_wchars))
1511 too_small := pe_emit_jcc8(mut rt.bytes, 0x73) // jae
1512 pe_emit_runtime_lea_rax_data(mut rt, buffer_off)
1513 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1514 rt.bytes << u8(0xc3) // ret
1515 fail_target := rt.bytes.len
1516 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1517 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1518 rt.bytes << u8(0xc3) // ret
1519 pe_patch_rel8(mut rt.bytes, null_name, fail_target)
1520 pe_patch_rel8(mut rt.bytes, missing, fail_target)
1521 pe_patch_rel8(mut rt.bytes, too_small, fail_target)
1522}
1523
1524fn pe_emit_runtime_calloc(mut rt PeRuntimeText) {
1525 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
1526 rt.bytes << [u8(0x48), 0xf7, 0xe2] // mul rdx
1527 rt.bytes << [u8(0x48), 0x85, 0xd2] // test rdx, rdx
1528 overflow := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1529 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1530 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x20] // mov [rsp+32], rax
1531 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1532 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1533 no_heap := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1534 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1535 rt.bytes << [u8(0xba), 0x08, 0, 0, 0] // mov edx, HEAP_ZERO_MEMORY
1536 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x20] // mov r8, [rsp+32]
1537 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x18] // add r8, 24
1538 size_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1539 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1540 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1541 alloc_failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1542 pe_emit_runtime_align_heap_allocated_data(mut rt)
1543 rt.bytes << [u8(0x4c), 0x89, 0xd8] // mov rax, r11
1544 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1545 rt.bytes << u8(0xc3) // ret
1546 fail_with_frame := rt.bytes.len
1547 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1548 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1549 rt.bytes << u8(0xc3) // ret
1550 fail_without_frame := rt.bytes.len
1551 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1552 rt.bytes << u8(0xc3) // ret
1553 pe_patch_rel8(mut rt.bytes, overflow, fail_without_frame)
1554 pe_patch_rel8(mut rt.bytes, no_heap, fail_with_frame)
1555 pe_patch_rel8(mut rt.bytes, size_overflow, fail_with_frame)
1556 pe_patch_rel8(mut rt.bytes, alloc_failed, fail_with_frame)
1557}
1558
1559fn pe_emit_runtime_malloc(mut rt PeRuntimeText) {
1560 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1561 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1562 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1563 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1564 no_heap := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1565 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1566 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1567 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x20] // mov r8, [rsp+32]
1568 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x18] // add r8, 24
1569 size_overflow := pe_emit_jcc8(mut rt.bytes, 0x72) // jc
1570 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1571 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1572 alloc_failed := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1573 pe_emit_runtime_align_heap_allocated_data(mut rt)
1574 rt.bytes << [u8(0x4c), 0x89, 0xd8] // mov rax, r11
1575 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1576 rt.bytes << u8(0xc3) // ret
1577 fail := rt.bytes.len
1578 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1579 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1580 rt.bytes << u8(0xc3) // ret
1581 pe_patch_rel8(mut rt.bytes, no_heap, fail)
1582 pe_patch_rel8(mut rt.bytes, size_overflow, fail)
1583 pe_patch_rel8(mut rt.bytes, alloc_failed, fail)
1584}
1585
1586fn pe_emit_runtime_free(mut rt PeRuntimeText) {
1587 pe_emit_runtime_aligned_free(mut rt)
1588}
1589
1590fn pe_emit_runtime_memcmp(mut rt PeRuntimeText) {
1591 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1592 zero_len := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1593 loop_start := rt.bytes.len
1594 rt.bytes << [u8(0x44), 0x8a, 0x09] // mov r9b, [rcx]
1595 rt.bytes << [u8(0x44), 0x8a, 0x12] // mov r10b, [rdx]
1596 rt.bytes << [u8(0x45), 0x38, 0xd1] // cmp r9b, r10b
1597 diff := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1598 rt.bytes << [u8(0x48), 0xff, 0xc1] // inc rcx
1599 rt.bytes << [u8(0x48), 0xff, 0xc2] // inc rdx
1600 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1601 more := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1602 done_equal := rt.bytes.len
1603 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1604 rt.bytes << u8(0xc3) // ret
1605 diff_target := rt.bytes.len
1606 rt.bytes << [u8(0x41), 0x0f, 0xb6, 0xc1] // movzx eax, r9b
1607 rt.bytes << [u8(0x45), 0x0f, 0xb6, 0xd2] // movzx r10d, r10b
1608 rt.bytes << [u8(0x44), 0x29, 0xd0] // sub eax, r10d
1609 rt.bytes << u8(0xc3) // ret
1610 pe_patch_rel8(mut rt.bytes, zero_len, done_equal)
1611 pe_patch_rel8(mut rt.bytes, diff, diff_target)
1612 pe_patch_rel8(mut rt.bytes, more, loop_start)
1613}
1614
1615fn pe_emit_runtime_atexit(mut rt PeRuntimeText, count_off u32, callbacks_off u32) {
1616 pe_emit_runtime_lea_rdx_data(mut rt, count_off)
1617 rt.bytes << [u8(0x48), 0x8b, 0x02] // mov rax, [rdx]
1618 rt.bytes << [u8(0x48), 0x83, 0xf8, pe_atexit_callback_capacity] // cmp rax, capacity
1619 overflow := pe_emit_jcc8(mut rt.bytes, 0x73) // jae
1620 pe_emit_runtime_lea_r8_data(mut rt, callbacks_off)
1621 rt.bytes << [u8(0x49), 0x89, 0x0c, 0xc0] // mov [r8 + rax * 8], rcx
1622 rt.bytes << [u8(0x48), 0xff, 0xc0] // inc rax
1623 rt.bytes << [u8(0x48), 0x89, 0x02] // mov [rdx], rax
1624 rt.bytes << [u8(0x31), 0xc0] // xor eax, eax
1625 rt.bytes << u8(0xc3) // ret
1626 overflow_target := rt.bytes.len
1627 rt.bytes << [u8(0xb8), 1, 0, 0, 0] // mov eax, 1
1628 rt.bytes << u8(0xc3) // ret
1629 pe_patch_rel8(mut rt.bytes, overflow, overflow_target)
1630}
1631
1632fn pe_emit_runtime_run_atexit(mut rt PeRuntimeText, count_off u32, callbacks_off u32) {
1633 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1634 loop_start := rt.bytes.len
1635 pe_emit_runtime_lea_rdx_data(mut rt, count_off)
1636 rt.bytes << [u8(0x48), 0x8b, 0x02] // mov rax, [rdx]
1637 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1638 done := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1639 rt.bytes << [u8(0x48), 0xff, 0xc8] // dec rax
1640 rt.bytes << [u8(0x48), 0x89, 0x02] // mov [rdx], rax
1641 pe_emit_runtime_lea_r8_data(mut rt, callbacks_off)
1642 rt.bytes << [u8(0x49), 0x8b, 0x0c, 0xc0] // mov rcx, [r8 + rax * 8]
1643 rt.bytes << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
1644 skip_null := pe_emit_jcc8(mut rt.bytes, 0x74) // je
1645 rt.bytes << [u8(0xff), 0xd1] // call rcx
1646 more := pe_emit_jcc8(mut rt.bytes, 0xeb) // jmp
1647 done_target := rt.bytes.len
1648 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x28] // add rsp, 40
1649 rt.bytes << u8(0xc3) // ret
1650 pe_patch_rel8(mut rt.bytes, done, done_target)
1651 pe_patch_rel8(mut rt.bytes, skip_null, loop_start)
1652 pe_patch_rel8(mut rt.bytes, more, loop_start)
1653}
1654
1655fn pe_emit_runtime_exit(mut rt PeRuntimeText, run_atexit_offset int) {
1656 rt.bytes << [u8(0x48), 0x83, 0xec, 0x28] // sub rsp, 40
1657 if run_atexit_offset >= 0 {
1658 rt.bytes << [u8(0x89), 0x4c, 0x24, 0x20] // mov [rsp+32], ecx
1659 call_field := pe_emit_call_placeholder(mut rt.bytes)
1660 rt.bytes << [u8(0x8b), 0x4c, 0x24, 0x20] // mov ecx, [rsp+32]
1661 pe_patch_rel32_local(mut rt.bytes, call_field, run_atexit_offset)
1662 }
1663 pe_emit_runtime_call_import(mut rt, 'ExitProcess')
1664 rt.bytes << u8(0xcc) // int3
1665}
1666
1667fn pe_emit_runtime_exit_process_1(mut rt PeRuntimeText) {
1668 rt.bytes << [u8(0xb9), 0x01, 0, 0, 0] // mov ecx, 1
1669 pe_emit_runtime_call_import(mut rt, 'ExitProcess')
1670 rt.bytes << u8(0xcc) // int3
1671}
1672
1673fn pe_emit_runtime_i64_str(mut rt PeRuntimeText) {
1674 rt.bytes << [u8(0x48), 0x83, 0xec, 0x58] // sub rsp, 88
1675 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1676 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1677 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1678 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1679 no_heap := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1680 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1681 rt.bytes << [u8(0xba), 0x08, 0, 0, 0] // mov edx, HEAP_ZERO_MEMORY
1682 rt.bytes << [u8(0x41), 0xb8, 0x20, 0, 0, 0] // mov r8d, 32
1683 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1684 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1685 alloc_failed := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1686 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x30] // mov [rsp+48], rax
1687 rt.bytes << [u8(0x4c), 0x8d, 0x40, 0x1f] // lea r8, [rax+31]
1688 rt.bytes << [u8(0x41), 0xc6, 0x00, 0x00] // mov byte ptr [r8], 0
1689 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x28] // mov rax, [rsp+40]
1690 rt.bytes << [u8(0x45), 0x31, 0xc9] // xor r9d, r9d
1691 rt.bytes << [u8(0x45), 0x31, 0xd2] // xor r10d, r10d
1692 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1693 non_negative := pe_emit_jcc32(mut rt.bytes, 0x89) // jns
1694 rt.bytes << [u8(0x41), 0xb2, 0x01] // mov r10b, 1
1695 rt.bytes << [u8(0x48), 0xf7, 0xd8] // neg rax
1696 non_negative_target := rt.bytes.len
1697 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1698 non_zero := pe_emit_jcc32(mut rt.bytes, 0x85) // jne
1699 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1700 rt.bytes << [u8(0x41), 0xc6, 0x00, 0x30] // mov byte ptr [r8], '0'
1701 rt.bytes << [u8(0x41), 0xb9, 0x01, 0, 0, 0] // mov r9d, 1
1702 maybe_sign_jump := pe_emit_jmp32(mut rt.bytes)
1703 loop_start := rt.bytes.len
1704 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
1705 rt.bytes << [u8(0xb9), 0x0a, 0, 0, 0] // mov ecx, 10
1706 rt.bytes << [u8(0x48), 0xf7, 0xf1] // div rcx
1707 rt.bytes << [u8(0x80), 0xc2, 0x30] // add dl, '0'
1708 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1709 rt.bytes << [u8(0x41), 0x88, 0x10] // mov [r8], dl
1710 rt.bytes << [u8(0x49), 0xff, 0xc1] // inc r9
1711 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1712 loop_more := pe_emit_jcc32(mut rt.bytes, 0x85) // jne
1713 maybe_sign := rt.bytes.len
1714 rt.bytes << [u8(0x45), 0x84, 0xd2] // test r10b, r10b
1715 done_digits := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1716 rt.bytes << [u8(0x49), 0xff, 0xc8] // dec r8
1717 rt.bytes << [u8(0x41), 0xc6, 0x00, 0x2d] // mov byte ptr [r8], '-'
1718 rt.bytes << [u8(0x49), 0xff, 0xc1] // inc r9
1719 done_digits_target := rt.bytes.len
1720 rt.bytes << [u8(0x48), 0x8b, 0x54, 0x24, 0x20] // mov rdx, [rsp+32]
1721 rt.bytes << [u8(0x4c), 0x89, 0x02] // mov [rdx], r8
1722 rt.bytes << [u8(0x44), 0x89, 0x4a, 0x08] // mov [rdx+8], r9d
1723 rt.bytes << [u8(0xc7), 0x42, 0x0c, 0, 0, 0, 0] // mov dword ptr [rdx+12], 0
1724 rt.bytes << [u8(0x48), 0x89, 0xd0] // mov rax, rdx
1725 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
1726 rt.bytes << u8(0xc3) // ret
1727 fail := rt.bytes.len
1728 pe_emit_runtime_exit_process_1(mut rt)
1729 pe_patch_rel32_local(mut rt.bytes, no_heap, fail)
1730 pe_patch_rel32_local(mut rt.bytes, alloc_failed, fail)
1731 pe_patch_rel32_local(mut rt.bytes, non_negative, non_negative_target)
1732 pe_patch_rel32_local(mut rt.bytes, non_zero, loop_start)
1733 pe_patch_rel32_local(mut rt.bytes, maybe_sign_jump, maybe_sign)
1734 pe_patch_rel32_local(mut rt.bytes, loop_more, loop_start)
1735 pe_patch_rel32_local(mut rt.bytes, done_digits, done_digits_target)
1736}
1737
1738fn pe_emit_runtime_string_plus(mut rt PeRuntimeText) {
1739 rt.bytes << [u8(0x48), 0x83, 0xec, 0x68] // sub rsp, 104
1740 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1741 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1742 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x30] // mov [rsp+48], r8
1743 rt.bytes << [u8(0x44), 0x8b, 0x4a, 0x08] // mov r9d, [rdx+8]
1744 rt.bytes << [u8(0x45), 0x03, 0x48, 0x08] // add r9d, [r8+8]
1745 len_overflow := pe_emit_jcc32(mut rt.bytes, 0x82) // jc
1746 rt.bytes << [u8(0x4c), 0x89, 0x4c, 0x24, 0x38] // mov [rsp+56], r9
1747 rt.bytes << [u8(0x45), 0x89, 0xc8] // mov r8d, r9d
1748 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x01] // add r8, 1
1749 alloc_overflow := pe_emit_jcc32(mut rt.bytes, 0x82) // jc
1750 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x40] // mov [rsp+64], r8
1751 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1752 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1753 no_heap := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1754 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1755 rt.bytes << [u8(0xba), 0x08, 0, 0, 0] // mov edx, HEAP_ZERO_MEMORY
1756 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x40] // mov r8, [rsp+64]
1757 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1758 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1759 alloc_failed := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1760 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x48] // mov [rsp+72], rax
1761 rt.bytes << [u8(0x48), 0x8b, 0x54, 0x24, 0x28] // mov rdx, [rsp+40]
1762 rt.bytes << [u8(0x4c), 0x8b, 0x12] // mov r10, [rdx]
1763 rt.bytes << [u8(0x44), 0x8b, 0x5a, 0x08] // mov r11d, [rdx+8]
1764 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1765 rt.bytes << [u8(0x4d), 0x85, 0xdb] // test r11, r11
1766 copy_a_done := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1767 copy_a_loop := rt.bytes.len
1768 rt.bytes << [u8(0x45), 0x8a, 0x0a] // mov r9b, [r10]
1769 rt.bytes << [u8(0x44), 0x88, 0x09] // mov [rcx], r9b
1770 rt.bytes << [u8(0x49), 0xff, 0xc2] // inc r10
1771 rt.bytes << [u8(0x48), 0xff, 0xc1] // inc rcx
1772 rt.bytes << [u8(0x49), 0xff, 0xcb] // dec r11
1773 copy_a_more := pe_emit_jcc32(mut rt.bytes, 0x85) // jne
1774 copy_a_done_target := rt.bytes.len
1775 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x30] // mov r8, [rsp+48]
1776 rt.bytes << [u8(0x4d), 0x8b, 0x10] // mov r10, [r8]
1777 rt.bytes << [u8(0x45), 0x8b, 0x58, 0x08] // mov r11d, [r8+8]
1778 rt.bytes << [u8(0x4d), 0x85, 0xdb] // test r11, r11
1779 copy_b_done := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1780 copy_b_loop := rt.bytes.len
1781 rt.bytes << [u8(0x45), 0x8a, 0x0a] // mov r9b, [r10]
1782 rt.bytes << [u8(0x44), 0x88, 0x09] // mov [rcx], r9b
1783 rt.bytes << [u8(0x49), 0xff, 0xc2] // inc r10
1784 rt.bytes << [u8(0x48), 0xff, 0xc1] // inc rcx
1785 rt.bytes << [u8(0x49), 0xff, 0xcb] // dec r11
1786 copy_b_more := pe_emit_jcc32(mut rt.bytes, 0x85) // jne
1787 copy_b_done_target := rt.bytes.len
1788 rt.bytes << [u8(0xc6), 0x01, 0x00] // mov byte ptr [rcx], 0
1789 rt.bytes << [u8(0x48), 0x8b, 0x54, 0x24, 0x20] // mov rdx, [rsp+32]
1790 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x48] // mov rax, [rsp+72]
1791 rt.bytes << [u8(0x48), 0x89, 0x02] // mov [rdx], rax
1792 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x38] // mov eax, [rsp+56]
1793 rt.bytes << [u8(0x89), 0x42, 0x08] // mov [rdx+8], eax
1794 rt.bytes << [u8(0xc7), 0x42, 0x0c, 0, 0, 0, 0] // mov dword ptr [rdx+12], 0
1795 rt.bytes << [u8(0x48), 0x89, 0xd0] // mov rax, rdx
1796 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x68] // add rsp, 104
1797 rt.bytes << u8(0xc3) // ret
1798 fail := rt.bytes.len
1799 pe_emit_runtime_exit_process_1(mut rt)
1800 pe_patch_rel32_local(mut rt.bytes, len_overflow, fail)
1801 pe_patch_rel32_local(mut rt.bytes, alloc_overflow, fail)
1802 pe_patch_rel32_local(mut rt.bytes, no_heap, fail)
1803 pe_patch_rel32_local(mut rt.bytes, alloc_failed, fail)
1804 pe_patch_rel32_local(mut rt.bytes, copy_a_done, copy_a_done_target)
1805 pe_patch_rel32_local(mut rt.bytes, copy_a_more, copy_a_loop)
1806 pe_patch_rel32_local(mut rt.bytes, copy_b_done, copy_b_done_target)
1807 pe_patch_rel32_local(mut rt.bytes, copy_b_more, copy_b_loop)
1808}
1809
1810fn pe_emit_runtime_errno(mut rt PeRuntimeText) {
1811 errno_off := pe_runtime_data_alloc(mut rt, 4, 4)
1812 pe_emit_runtime_lea_rax_data(mut rt, errno_off)
1813 rt.bytes << u8(0xc3) // ret
1814}
1815
1816fn pe_emit_runtime_new_array_from_c_array_noscan(mut rt PeRuntimeText) {
1817 rt.bytes << [u8(0x48), 0x83, 0xec, 0x58] // sub rsp, 88
1818 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1819 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1820 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x30] // mov [rsp+48], r8
1821 rt.bytes << [u8(0x4c), 0x89, 0x4c, 0x24, 0x38] // mov [rsp+56], r9
1822 rt.bytes << [u8(0x48), 0x8b, 0x84, 0x24, 0x80, 0, 0, 0] // mov rax, [rsp+128]
1823 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x40] // mov [rsp+64], rax
1824 rt.bytes << [u8(0x48), 0xc7, 0x01, 0, 0, 0, 0] // mov qword ptr [rcx], 0
1825 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x08, 0, 0, 0, 0] // mov qword ptr [rcx+8], 0
1826 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x10, 0, 0, 0, 0] // mov qword ptr [rcx+16], 0
1827 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x18, 0, 0, 0, 0] // mov qword ptr [rcx+24], 0
1828 rt.bytes << [u8(0x83), 0xfa, 0x00] // cmp edx, 0
1829 negative_len := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1830 rt.bytes << [u8(0x41), 0x83, 0xf8, 0x00] // cmp r8d, 0
1831 negative_cap := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1832 rt.bytes << [u8(0x41), 0x83, 0xf9, 0x00] // cmp r9d, 0
1833 negative_elem_size := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1834 rt.bytes << [u8(0x45), 0x89, 0xc2] // mov r10d, r8d
1835 rt.bytes << [u8(0x41), 0x39, 0xd2] // cmp r10d, edx
1836 cap_ready := pe_emit_jcc8(mut rt.bytes, 0x7d) // jge
1837 rt.bytes << [u8(0x41), 0x89, 0xd2] // mov r10d, edx
1838 cap_ready_target := rt.bytes.len
1839 rt.bytes << [u8(0x4c), 0x89, 0x54, 0x24, 0x48] // mov [rsp+72], r10
1840 rt.bytes << [u8(0x49), 0x63, 0xc2] // movsxd rax, r10d
1841 rt.bytes << [u8(0x4d), 0x63, 0xd9] // movsxd r11, r9d
1842 rt.bytes << [u8(0x49), 0x0f, 0xaf, 0xc3] // imul rax, r11
1843 rt.bytes << [u8(0x49), 0x89, 0xc0] // mov r8, rax
1844 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1845 payload_nonzero := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1846 rt.bytes << [u8(0x41), 0xb8, 0x01, 0, 0, 0] // mov r8d, 1
1847 payload_nonzero_target := rt.bytes.len
1848 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x20] // add r8, 32
1849 allocation_overflow := pe_emit_jcc32(mut rt.bytes, 0x82) // jc
1850 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x50] // mov [rsp+80], r8
1851 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1852 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1853 no_heap := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1854 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1855 rt.bytes << [u8(0xba), 0x08, 0, 0, 0] // mov edx, HEAP_ZERO_MEMORY
1856 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x50] // mov r8, [rsp+80]
1857 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1858 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1859 alloc_failed := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1860 pe_emit_runtime_align_heap_allocated_data(mut rt)
1861 rt.bytes << [u8(0x41), 0xc6, 0x03, 0] // mov byte ptr [r11], 0 ; ArrayDataHeader.has_slices = false
1862 rt.bytes << [u8(0x48), 0x8b, 0x4c, 0x24, 0x20] // mov rcx, [rsp+32]
1863 rt.bytes << [u8(0x49), 0x8d, 0x43, 0x08] // lea rax, [r11+8]
1864 rt.bytes << [u8(0x48), 0x89, 0x01] // mov [rcx], rax
1865 rt.bytes << [u8(0xc7), 0x41, 0x08, 0, 0, 0, 0] // mov dword ptr [rcx+8], 0
1866 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x28] // mov eax, [rsp+40]
1867 rt.bytes << [u8(0x89), 0x41, 0x0c] // mov [rcx+12], eax
1868 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x48] // mov eax, [rsp+72]
1869 rt.bytes << [u8(0x89), 0x41, 0x10] // mov [rcx+16], eax
1870 rt.bytes << [u8(0xc7), 0x41, 0x14, 0x30, 0, 0, 0] // mov dword ptr [rcx+20], 48
1871 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x38] // mov eax, [rsp+56]
1872 rt.bytes << [u8(0x89), 0x41, 0x18] // mov [rcx+24], eax
1873 rt.bytes << [u8(0x48), 0x63, 0x44, 0x24, 0x28] // movsxd rax, dword ptr [rsp+40]
1874 rt.bytes << [u8(0x4c), 0x63, 0x54, 0x24, 0x38] // movsxd r10, dword ptr [rsp+56]
1875 rt.bytes << [u8(0x49), 0x0f, 0xaf, 0xc2] // imul rax, r10
1876 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1877 no_copy := pe_emit_jcc32(mut rt.bytes, 0x8e) // jle
1878 rt.bytes << [u8(0x4c), 0x8b, 0x54, 0x24, 0x40] // mov r10, [rsp+64]
1879 rt.bytes << [u8(0x4d), 0x85, 0xd2] // test r10, r10
1880 null_source := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1881 rt.bytes << [u8(0x4d), 0x8d, 0x43, 0x08] // lea r8, [r11+8]
1882 copy_loop := rt.bytes.len
1883 rt.bytes << [u8(0x41), 0x8a, 0x12] // mov dl, [r10]
1884 rt.bytes << [u8(0x41), 0x88, 0x10] // mov [r8], dl
1885 rt.bytes << [u8(0x49), 0xff, 0xc2] // inc r10
1886 rt.bytes << [u8(0x49), 0xff, 0xc0] // inc r8
1887 rt.bytes << [u8(0x48), 0xff, 0xc8] // dec rax
1888 copy_more := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1889 done := rt.bytes.len
1890 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
1891 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
1892 rt.bytes << u8(0xc3) // ret
1893 fail := rt.bytes.len
1894 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
1895 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
1896 rt.bytes << u8(0xc3) // ret
1897 pe_patch_rel32_local(mut rt.bytes, negative_len, fail)
1898 pe_patch_rel32_local(mut rt.bytes, negative_cap, fail)
1899 pe_patch_rel32_local(mut rt.bytes, negative_elem_size, fail)
1900 pe_patch_rel8(mut rt.bytes, cap_ready, cap_ready_target)
1901 pe_patch_rel8(mut rt.bytes, payload_nonzero, payload_nonzero_target)
1902 pe_patch_rel32_local(mut rt.bytes, allocation_overflow, fail)
1903 pe_patch_rel32_local(mut rt.bytes, no_heap, fail)
1904 pe_patch_rel32_local(mut rt.bytes, alloc_failed, fail)
1905 pe_patch_rel32_local(mut rt.bytes, no_copy, done)
1906 pe_patch_rel32_local(mut rt.bytes, null_source, done)
1907 pe_patch_rel8(mut rt.bytes, copy_more, copy_loop)
1908}
1909
1910fn pe_emit_runtime_new_array_noscan(mut rt PeRuntimeText) {
1911 rt.bytes << [u8(0x48), 0x83, 0xec, 0x58] // sub rsp, 88
1912 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1913 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1914 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x30] // mov [rsp+48], r8
1915 rt.bytes << [u8(0x4c), 0x89, 0x4c, 0x24, 0x38] // mov [rsp+56], r9
1916 rt.bytes << [u8(0x48), 0xc7, 0x01, 0, 0, 0, 0] // mov qword ptr [rcx], 0
1917 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x08, 0, 0, 0, 0] // mov qword ptr [rcx+8], 0
1918 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x10, 0, 0, 0, 0] // mov qword ptr [rcx+16], 0
1919 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x18, 0, 0, 0, 0] // mov qword ptr [rcx+24], 0
1920 rt.bytes << [u8(0x83), 0xfa, 0x00] // cmp edx, 0
1921 negative_len := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1922 rt.bytes << [u8(0x41), 0x83, 0xf8, 0x00] // cmp r8d, 0
1923 negative_cap := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1924 rt.bytes << [u8(0x41), 0x83, 0xf9, 0x00] // cmp r9d, 0
1925 negative_elem_size := pe_emit_jcc32(mut rt.bytes, 0x8c) // jl
1926 rt.bytes << [u8(0x45), 0x89, 0xc2] // mov r10d, r8d
1927 rt.bytes << [u8(0x41), 0x39, 0xd2] // cmp r10d, edx
1928 cap_ready := pe_emit_jcc8(mut rt.bytes, 0x7d) // jge
1929 rt.bytes << [u8(0x41), 0x89, 0xd2] // mov r10d, edx
1930 cap_ready_target := rt.bytes.len
1931 rt.bytes << [u8(0x4c), 0x89, 0x54, 0x24, 0x40] // mov [rsp+64], r10
1932 rt.bytes << [u8(0x49), 0x63, 0xc2] // movsxd rax, r10d
1933 rt.bytes << [u8(0x4d), 0x63, 0xd9] // movsxd r11, r9d
1934 rt.bytes << [u8(0x49), 0x0f, 0xaf, 0xc3] // imul rax, r11
1935 rt.bytes << [u8(0x49), 0x89, 0xc0] // mov r8, rax
1936 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1937 payload_nonzero := pe_emit_jcc8(mut rt.bytes, 0x75) // jne
1938 rt.bytes << [u8(0x41), 0xb8, 0x01, 0, 0, 0] // mov r8d, 1
1939 payload_nonzero_target := rt.bytes.len
1940 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x20] // add r8, 32
1941 allocation_overflow := pe_emit_jcc32(mut rt.bytes, 0x82) // jc
1942 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x48] // mov [rsp+72], r8
1943 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
1944 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1945 no_heap := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1946 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
1947 rt.bytes << [u8(0xba), 0x08, 0, 0, 0] // mov edx, HEAP_ZERO_MEMORY
1948 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x48] // mov r8, [rsp+72]
1949 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
1950 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
1951 alloc_failed := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1952 pe_emit_runtime_align_heap_allocated_data(mut rt)
1953 rt.bytes << [u8(0x41), 0xc6, 0x03, 0] // mov byte ptr [r11], 0 ; ArrayDataHeader.has_slices = false
1954 rt.bytes << [u8(0x48), 0x8b, 0x4c, 0x24, 0x20] // mov rcx, [rsp+32]
1955 rt.bytes << [u8(0x49), 0x8d, 0x43, 0x08] // lea rax, [r11+8]
1956 rt.bytes << [u8(0x48), 0x89, 0x01] // mov [rcx], rax
1957 rt.bytes << [u8(0xc7), 0x41, 0x08, 0, 0, 0, 0] // mov dword ptr [rcx+8], 0
1958 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x28] // mov eax, [rsp+40]
1959 rt.bytes << [u8(0x89), 0x41, 0x0c] // mov [rcx+12], eax
1960 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x40] // mov eax, [rsp+64]
1961 rt.bytes << [u8(0x89), 0x41, 0x10] // mov [rcx+16], eax
1962 rt.bytes << [u8(0xc7), 0x41, 0x14, 0x30, 0, 0, 0] // mov dword ptr [rcx+20], 48
1963 rt.bytes << [u8(0x8b), 0x44, 0x24, 0x38] // mov eax, [rsp+56]
1964 rt.bytes << [u8(0x89), 0x41, 0x18] // mov [rcx+24], eax
1965 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
1966 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
1967 rt.bytes << u8(0xc3) // ret
1968 fail := rt.bytes.len
1969 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
1970 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
1971 rt.bytes << u8(0xc3) // ret
1972 pe_patch_rel32_local(mut rt.bytes, negative_len, fail)
1973 pe_patch_rel32_local(mut rt.bytes, negative_cap, fail)
1974 pe_patch_rel32_local(mut rt.bytes, negative_elem_size, fail)
1975 pe_patch_rel8(mut rt.bytes, cap_ready, cap_ready_target)
1976 pe_patch_rel8(mut rt.bytes, payload_nonzero, payload_nonzero_target)
1977 pe_patch_rel32_local(mut rt.bytes, allocation_overflow, fail)
1978 pe_patch_rel32_local(mut rt.bytes, no_heap, fail)
1979 pe_patch_rel32_local(mut rt.bytes, alloc_failed, fail)
1980}
1981
1982fn pe_emit_runtime_array_rune_string(mut rt PeRuntimeText) {
1983 rt.bytes << [u8(0x48), 0x83, 0xec, 0x58] // sub rsp, 88
1984 rt.bytes << [u8(0x48), 0x89, 0x4c, 0x24, 0x20] // mov [rsp+32], rcx
1985 rt.bytes << [u8(0x48), 0x89, 0x54, 0x24, 0x28] // mov [rsp+40], rdx
1986 rt.bytes << [u8(0x48), 0xc7, 0x01, 0, 0, 0, 0] // mov qword ptr [rcx], 0
1987 rt.bytes << [u8(0x48), 0xc7, 0x41, 0x08, 0, 0, 0, 0] // mov qword ptr [rcx+8], 0
1988 rt.bytes << [u8(0x48), 0x85, 0xd2] // test rdx, rdx
1989 null_array := pe_emit_jcc32(mut rt.bytes, 0x84) // je
1990 rt.bytes << [u8(0x48), 0x63, 0x42, 0x0c] // movsxd rax, dword ptr [rdx+12]
1991 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x30] // mov [rsp+48], rax
1992 rt.bytes << [u8(0x48), 0x8b, 0x02] // mov rax, [rdx]
1993 rt.bytes << [u8(0x4c), 0x63, 0x52, 0x08] // movsxd r10, dword ptr [rdx+8]
1994 rt.bytes << [u8(0x4c), 0x01, 0xd0] // add rax, r10
1995 rt.bytes << [u8(0x48), 0x89, 0x44, 0x24, 0x38] // mov [rsp+56], rax
1996 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x30] // mov r8, [rsp+48]
1997 rt.bytes << [u8(0x4d), 0x85, 0xc0] // test r8, r8
1998 positive_size := pe_emit_jcc32(mut rt.bytes, 0x8f) // jg
1999 rt.bytes << [u8(0x41), 0xb8, 0x01, 0, 0, 0] // mov r8d, 1
2000 size_ready_jump := pe_emit_jmp32(mut rt.bytes)
2001 positive_size_target := rt.bytes.len
2002 rt.bytes << [u8(0x49), 0xc1, 0xe0, 0x02] // shl r8, 2
2003 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x01] // add r8, 1
2004 size_ready := rt.bytes.len
2005 rt.bytes << [u8(0x49), 0x83, 0xc0, 0x18] // add r8, 24
2006 allocation_overflow := pe_emit_jcc32(mut rt.bytes, 0x82) // jc
2007 rt.bytes << [u8(0x4c), 0x89, 0x44, 0x24, 0x50] // mov [rsp+80], r8
2008 pe_emit_runtime_call_import(mut rt, 'GetProcessHeap')
2009 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
2010 no_heap := pe_emit_jcc32(mut rt.bytes, 0x84) // je
2011 rt.bytes << [u8(0x48), 0x89, 0xc1] // mov rcx, rax
2012 rt.bytes << [u8(0x31), 0xd2] // xor edx, edx
2013 rt.bytes << [u8(0x4c), 0x8b, 0x44, 0x24, 0x50] // mov r8, [rsp+80]
2014 pe_emit_runtime_call_import(mut rt, 'HeapAlloc')
2015 rt.bytes << [u8(0x48), 0x85, 0xc0] // test rax, rax
2016 alloc_failed := pe_emit_jcc32(mut rt.bytes, 0x84) // je
2017 pe_emit_runtime_align_heap_allocated_data(mut rt)
2018 rt.bytes << [u8(0x4c), 0x89, 0x5c, 0x24, 0x40] // mov [rsp+64], r11
2019 rt.bytes << [u8(0x48), 0x8b, 0x4c, 0x24, 0x30] // mov rcx, [rsp+48]
2020 rt.bytes << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
2021 no_input := pe_emit_jcc32(mut rt.bytes, 0x8e) // jle
2022 rt.bytes << [u8(0x48), 0x8b, 0x54, 0x24, 0x38] // mov rdx, [rsp+56]
2023 rt.bytes << [u8(0x48), 0x85, 0xd2] // test rdx, rdx
2024 no_data := pe_emit_jcc32(mut rt.bytes, 0x84) // je
2025 loop_start := rt.bytes.len
2026 rt.bytes << [u8(0x8b), 0x02] // mov eax, [rdx]
2027 rt.bytes << [u8(0x48), 0x83, 0xc2, 0x04] // add rdx, 4
2028 rt.bytes << [u8(0x83), 0xf8, 0x7f] // cmp eax, 0x7f
2029 two_byte_check := pe_emit_jcc32(mut rt.bytes, 0x87) // ja
2030 rt.bytes << [u8(0x41), 0x88, 0x03] // mov [r11], al
2031 rt.bytes << [u8(0x49), 0xff, 0xc3] // inc r11
2032 after_one := pe_emit_jmp32(mut rt.bytes)
2033 two_byte_target := rt.bytes.len
2034 rt.bytes << [u8(0x3d), 0xff, 0x07, 0, 0] // cmp eax, 0x7ff
2035 three_byte_check := pe_emit_jcc32(mut rt.bytes, 0x87) // ja
2036 rt.bytes << [u8(0x41), 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x06, 0x41, 0x80, 0xc9, 0xc0, 0x45, 0x88,
2037 0x0b, 0x41, 0x89, 0xc1, 0x41, 0x80, 0xe1, 0x3f, 0x41, 0x80, 0xc9, 0x80, 0x45, 0x88, 0x4b,
2038 0x01, 0x49, 0x83, 0xc3, 0x02]
2039 after_two := pe_emit_jmp32(mut rt.bytes)
2040 three_byte_target := rt.bytes.len
2041 rt.bytes << [u8(0x3d), 0x00, 0xd8, 0, 0] // cmp eax, 0xd800
2042 valid_three := pe_emit_jcc32(mut rt.bytes, 0x82) // jb
2043 rt.bytes << [u8(0x3d), 0xff, 0xdf, 0, 0] // cmp eax, 0xdfff
2044 skip_surrogate := pe_emit_jcc32(mut rt.bytes, 0x86) // jbe
2045 valid_three_target := rt.bytes.len
2046 rt.bytes << [u8(0x3d), 0xff, 0xff, 0, 0] // cmp eax, 0xffff
2047 four_byte_check := pe_emit_jcc32(mut rt.bytes, 0x87) // ja
2048 rt.bytes << [u8(0x41), 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x0c, 0x41, 0x80, 0xc9, 0xe0, 0x45, 0x88,
2049 0x0b, 0x41, 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x06, 0x41, 0x80, 0xe1, 0x3f, 0x41, 0x80, 0xc9,
2050 0x80, 0x45, 0x88, 0x4b, 0x01, 0x41, 0x89, 0xc1, 0x41, 0x80, 0xe1, 0x3f, 0x41, 0x80, 0xc9,
2051 0x80, 0x45, 0x88, 0x4b, 0x02, 0x49, 0x83, 0xc3, 0x03]
2052 after_three := pe_emit_jmp32(mut rt.bytes)
2053 four_byte_target := rt.bytes.len
2054 rt.bytes << [u8(0x3d), 0xff, 0xff, 0x10, 0] // cmp eax, 0x10ffff
2055 skip_large := pe_emit_jcc32(mut rt.bytes, 0x87) // ja
2056 rt.bytes << [u8(0x41), 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x12, 0x41, 0x80, 0xc9, 0xf0, 0x45, 0x88,
2057 0x0b, 0x41, 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x0c, 0x41, 0x80, 0xe1, 0x3f, 0x41, 0x80, 0xc9,
2058 0x80, 0x45, 0x88, 0x4b, 0x01, 0x41, 0x89, 0xc1, 0x41, 0xc1, 0xe9, 0x06, 0x41, 0x80, 0xe1,
2059 0x3f, 0x41, 0x80, 0xc9, 0x80, 0x45, 0x88, 0x4b, 0x02, 0x41, 0x89, 0xc1, 0x41, 0x80, 0xe1,
2060 0x3f, 0x41, 0x80, 0xc9, 0x80, 0x45, 0x88, 0x4b, 0x03, 0x49, 0x83, 0xc3, 0x04]
2061 after_emit := rt.bytes.len
2062 rt.bytes << [u8(0x48), 0xff, 0xc9] // dec rcx
2063 more := pe_emit_jcc32(mut rt.bytes, 0x85) // jne
2064 finish := rt.bytes.len
2065 rt.bytes << [u8(0x41), 0xc6, 0x03, 0x00] // mov byte ptr [r11], 0
2066 rt.bytes << [u8(0x48), 0x8b, 0x4c, 0x24, 0x20] // mov rcx, [rsp+32]
2067 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x40] // mov rax, [rsp+64]
2068 rt.bytes << [u8(0x48), 0x89, 0x01] // mov [rcx], rax
2069 rt.bytes << [u8(0x4c), 0x89, 0xda] // mov rdx, r11
2070 rt.bytes << [u8(0x48), 0x29, 0xc2] // sub rdx, rax
2071 rt.bytes << [u8(0x89), 0x51, 0x08] // mov [rcx+8], edx
2072 rt.bytes << [u8(0xc7), 0x41, 0x0c, 0, 0, 0, 0] // mov dword ptr [rcx+12], 0
2073 rt.bytes << [u8(0x48), 0x89, 0xc8] // mov rax, rcx
2074 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
2075 rt.bytes << u8(0xc3) // ret
2076 fail := rt.bytes.len
2077 rt.bytes << [u8(0x48), 0x8b, 0x44, 0x24, 0x20] // mov rax, [rsp+32]
2078 rt.bytes << [u8(0x48), 0x83, 0xc4, 0x58] // add rsp, 88
2079 rt.bytes << u8(0xc3) // ret
2080 pe_patch_rel32_local(mut rt.bytes, null_array, fail)
2081 pe_patch_rel32_local(mut rt.bytes, positive_size, positive_size_target)
2082 pe_patch_rel32_local(mut rt.bytes, size_ready_jump, size_ready)
2083 pe_patch_rel32_local(mut rt.bytes, allocation_overflow, fail)
2084 pe_patch_rel32_local(mut rt.bytes, no_heap, fail)
2085 pe_patch_rel32_local(mut rt.bytes, alloc_failed, fail)
2086 pe_patch_rel32_local(mut rt.bytes, no_input, finish)
2087 pe_patch_rel32_local(mut rt.bytes, no_data, finish)
2088 pe_patch_rel32_local(mut rt.bytes, two_byte_check, two_byte_target)
2089 pe_patch_rel32_local(mut rt.bytes, after_one, after_emit)
2090 pe_patch_rel32_local(mut rt.bytes, three_byte_check, three_byte_target)
2091 pe_patch_rel32_local(mut rt.bytes, after_two, after_emit)
2092 pe_patch_rel32_local(mut rt.bytes, valid_three, valid_three_target)
2093 pe_patch_rel32_local(mut rt.bytes, skip_surrogate, after_emit)
2094 pe_patch_rel32_local(mut rt.bytes, four_byte_check, four_byte_target)
2095 pe_patch_rel32_local(mut rt.bytes, after_three, after_emit)
2096 pe_patch_rel32_local(mut rt.bytes, skip_large, after_emit)
2097 pe_patch_rel32_local(mut rt.bytes, more, loop_start)
2098}
2099
2100fn pe_read_i32_le(data []u8, off int) i32 {
2101 return i32(u32(data[off]) | (u32(data[off + 1]) << 8) | (u32(data[off + 2]) << 16) | (u32(data[
2102 off + 3]) << 24))
2103}
2104
2105fn pe_put_u32_le(mut data []u8, off int, v u32) {
2106 data[off] = u8(v)
2107 data[off + 1] = u8(v >> 8)
2108 data[off + 2] = u8(v >> 16)
2109 data[off + 3] = u8(v >> 24)
2110}
2111
2112fn pe_put_u64_le(mut data []u8, off int, v u64) {
2113 pe_put_u32_le(mut data, off, u32(v))
2114 pe_put_u32_le(mut data, off + 4, u32(v >> 32))
2115}
2116