| 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 | |
| 5 | module x64 |
| 6 | |
| 7 | import os |
| 8 | |
| 9 | const pe_dos_stub_size = 0x80 |
| 10 | const pe_signature = u32(0x00004550) |
| 11 | const pe_optional_header64_magic = u16(0x20b) |
| 12 | const pe_file_alignment = 0x200 |
| 13 | const pe_section_alignment = 0x1000 |
| 14 | const pe_image_base = u64(0x140000000) |
| 15 | const pe_size_of_optional_header64 = u16(0xf0) |
| 16 | const 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. |
| 20 | const pe_linker_major_version = u8(0) |
| 21 | const pe_linker_minor_version = u8(1) |
| 22 | const pe_major_operating_system_version = u16(6) |
| 23 | const pe_minor_operating_system_version = u16(0) |
| 24 | const pe_major_subsystem_version = u16(6) |
| 25 | const pe_minor_subsystem_version = u16(0) |
| 26 | const pe_size_of_stack_reserve = u64(0x100000) |
| 27 | const pe_size_of_stack_commit = u64(0x1000) |
| 28 | const pe_size_of_heap_reserve = u64(0x100000) |
| 29 | const pe_size_of_heap_commit = u64(0x1000) |
| 30 | |
| 31 | const pe_image_file_relocs_stripped = u16(0x0001) |
| 32 | const pe_image_file_executable_image = u16(0x0002) |
| 33 | const pe_image_file_large_address_aware = u16(0x0020) |
| 34 | const pe_image_subsystem_windows_cui = u16(3) |
| 35 | const pe_dll_characteristics_nx_compat = u16(0x0100) |
| 36 | |
| 37 | const pe_image_scn_cnt_code = u32(0x00000020) |
| 38 | const pe_image_scn_cnt_initialized_data = u32(0x00000040) |
| 39 | const pe_image_scn_mem_execute = u32(0x20000000) |
| 40 | const pe_image_scn_mem_read = u32(0x40000000) |
| 41 | const pe_image_scn_mem_write = u32(0x80000000) |
| 42 | |
| 43 | const pe_import_directory_index = 1 |
| 44 | const pe_exception_directory_index = 3 |
| 45 | const pe_base_reloc_directory_index = 5 |
| 46 | const pe_iat_directory_index = 12 |
| 47 | const pe_runtime_data_alignment = 16 |
| 48 | const pe_wgetenv_buffer_wchars = 32768 |
| 49 | const pe_wgetenv_buffer_bytes = pe_wgetenv_buffer_wchars * 2 |
| 50 | const pe_atexit_callback_capacity = 64 |
| 51 | const pe_run_atexit_symbol = '__v2_pe_run_atexit' |
| 52 | |
| 53 | const pe_kernel32_dll = 'kernel32.dll' |
| 54 | const pe_shell32_dll = 'shell32.dll' |
| 55 | const pe_ucrtbase_dll = 'ucrtbase.dll' |
| 56 | const pe_msvcrt_dll = 'msvcrt.dll' |
| 57 | const 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'] |
| 63 | const pe_shell32_imports = ['CommandLineToArgvW'] |
| 64 | const pe_ucrtbase_imports = ['log', 'ldexp', 'sqrt', '_time64', '_localtime64'] |
| 65 | const pe_msvcrt_imports = ['_scprintf', '_snprintf'] |
| 66 | |
| 67 | struct PeImport { |
| 68 | dll string |
| 69 | name string |
| 70 | } |
| 71 | |
| 72 | struct PeRuntimeCallPatch { |
| 73 | field_off int |
| 74 | dll string |
| 75 | name string |
| 76 | } |
| 77 | |
| 78 | struct PeRuntimeDataPatch { |
| 79 | field_off int |
| 80 | data_off u32 |
| 81 | } |
| 82 | |
| 83 | struct PeRuntimeText { |
| 84 | mut: |
| 85 | bytes []u8 |
| 86 | data []u8 |
| 87 | symbols map[string]u32 |
| 88 | import_patches []PeRuntimeCallPatch |
| 89 | data_patches []PeRuntimeDataPatch |
| 90 | } |
| 91 | |
| 92 | struct PeSection { |
| 93 | mut: |
| 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 | |
| 103 | struct 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 | |
| 112 | struct PeImportGroup { |
| 113 | dll string |
| 114 | start int |
| 115 | mut: |
| 116 | len int |
| 117 | } |
| 118 | |
| 119 | struct PeTextBuild { |
| 120 | data []u8 |
| 121 | argc_global_disp_off int |
| 122 | argv_global_disp_off int |
| 123 | } |
| 124 | |
| 125 | pub struct PeLinker { |
| 126 | coff &CoffObject |
| 127 | mut: |
| 128 | imports []PeImport |
| 129 | } |
| 130 | |
| 131 | pub fn PeLinker.new(coff &CoffObject) &PeLinker { |
| 132 | return unsafe { |
| 133 | &PeLinker{ |
| 134 | coff: coff |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | pub 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 | |
| 145 | pub 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 | |
| 265 | fn (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 | |
| 348 | fn (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 | |
| 458 | fn (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 | |
| 536 | fn (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 | |
| 548 | fn pe_import_key(dll string, name string) string { |
| 549 | return '${dll}:${name}' |
| 550 | } |
| 551 | |
| 552 | fn pe_kernel32_import_key(name string) string { |
| 553 | return pe_import_key(pe_kernel32_dll, name) |
| 554 | } |
| 555 | |
| 556 | fn pe_shell32_import_key(name string) string { |
| 557 | return pe_import_key(pe_shell32_dll, name) |
| 558 | } |
| 559 | |
| 560 | fn pe_ucrtbase_import_key(name string) string { |
| 561 | return pe_import_key(pe_ucrtbase_dll, name) |
| 562 | } |
| 563 | |
| 564 | fn pe_msvcrt_import_key(name string) string { |
| 565 | return pe_import_key(pe_msvcrt_dll, name) |
| 566 | } |
| 567 | |
| 568 | fn 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 | |
| 575 | fn 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 | |
| 584 | fn 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 | |
| 599 | fn 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 | |
| 606 | fn 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 | |
| 615 | fn 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 | |
| 633 | fn 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 | |
| 640 | fn 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 | |
| 649 | fn 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 | |
| 656 | fn 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 | |
| 665 | fn (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 | |
| 673 | fn (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 | |
| 747 | fn 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 | |
| 763 | fn (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 | |
| 833 | fn (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 | |
| 852 | fn (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 | |
| 857 | fn (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 | |
| 878 | fn (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 | |
| 882 | fn (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 | |
| 891 | fn (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 | |
| 900 | fn (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 | |
| 916 | fn (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 | |
| 922 | fn (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 | |
| 936 | fn (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 | |
| 945 | fn (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 | |
| 958 | fn (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 | |
| 972 | fn (l PeLinker) has_main_argc_data_symbol() bool { |
| 973 | if _ := l.main_argc_data_offset() { |
| 974 | return true |
| 975 | } |
| 976 | return false |
| 977 | } |
| 978 | |
| 979 | fn (l PeLinker) has_main_argv_data_symbol() bool { |
| 980 | if _ := l.main_argv_data_offset() { |
| 981 | return true |
| 982 | } |
| 983 | return false |
| 984 | } |
| 985 | |
| 986 | fn (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 | |
| 995 | fn (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 | |
| 1004 | fn (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 | |
| 1025 | fn (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 | |
| 1074 | fn 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 | |
| 1082 | fn 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 | |
| 1095 | fn 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 | |
| 1113 | fn 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 | |
| 1118 | fn 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 | |
| 1130 | fn 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 | |
| 1135 | fn 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 | |
| 1145 | fn 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 | |
| 1152 | fn 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 | |
| 1159 | fn 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 | |
| 1169 | fn 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 | |
| 1181 | fn 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 | |
| 1190 | fn 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 | |
| 1199 | fn 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 | |
| 1208 | fn 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 | |
| 1215 | fn 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 | |
| 1222 | fn 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 | |
| 1229 | fn 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 | |
| 1235 | fn 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 | |
| 1239 | fn 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 | |
| 1244 | fn 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 | |
| 1297 | fn 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 | |
| 1318 | fn 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 | |
| 1386 | fn 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 | |
| 1393 | fn 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 | |
| 1431 | fn 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 | |
| 1447 | fn 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 | |
| 1461 | fn 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 | |
| 1476 | fn 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 | |
| 1498 | fn 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 | |
| 1524 | fn 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 | |
| 1559 | fn 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 | |
| 1586 | fn pe_emit_runtime_free(mut rt PeRuntimeText) { |
| 1587 | pe_emit_runtime_aligned_free(mut rt) |
| 1588 | } |
| 1589 | |
| 1590 | fn 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 | |
| 1615 | fn 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 | |
| 1632 | fn 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 | |
| 1655 | fn 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 | |
| 1667 | fn 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 | |
| 1673 | fn 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 | |
| 1738 | fn 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 | |
| 1810 | fn 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 | |
| 1816 | fn 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 | |
| 1910 | fn 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 | |
| 1982 | fn 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 | |
| 2100 | fn 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 | |
| 2105 | fn 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 | |
| 2112 | fn 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 | |