From f35b738da28cbd92ea591995a85036aa8ca372da Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 21 Jun 2025 23:01:07 -0400 Subject: [PATCH] realizing a convention... --- code/asm/hello_files.asm | 274 +++++++++++++++++++++------------- code/asm/hello_files.proj | 6 +- scripts/build_hello_files.ps1 | 37 +++-- scripts/build_hello_nasm.ps1 | 16 +- 4 files changed, 213 insertions(+), 120 deletions(-) diff --git a/code/asm/hello_files.asm b/code/asm/hello_files.asm index fca2e18..662541d 100644 --- a/code/asm/hello_files.asm +++ b/code/asm/hello_files.asm @@ -194,10 +194,10 @@ DEFAULT REL ; Use RIP-relative addressing by default ;region Memory %define nullptr 0 -%define kilo 1024 +%define kilo 1024 ; Usage: def_array -%macro def_farray 2+ +%macro def_farray 2 struc %1 .ptr: resb %2 endstruc @@ -237,11 +237,8 @@ endstruc def_Slice Byte ; Usage: stack_slice %1: , %2 , %3 -; Requires a `stack_offset` variable to be %assign'd to 0 at the start of a scope. -; The user must then `sub rsp, stack_offset` to allocate the space. %macro stack_slice 2 - %assign stack_offset stack_offset + %1 %+ _size - %define %2 (rstack_base_ptr - stack_offset) + call_frame_alloc %1 %+ _size %endmacro ; Usage: slice_assert %1: Slice_ { .ptr = Slice.ptr, .len = Slice.len } @@ -282,6 +279,40 @@ def_Slice Byte mov rstack_ptr, rstack_base_ptr pop rstack_base_ptr %endmacro + +; We will still use R11 as a temporary accumulator. + +; Usage: begin_call_prep +; Initializes the accumulator and reserves 8 bytes in the frame +; to store the total frame size itself. +%macro call_frame 0 + xor r11, r11 ; Clear the accumulator register to 0 + add r11, 8 ; Reserve 8 bytes for the size storage +%endmacro + +; Usage: stack_alloc +%macro call_frame_alloc 1 + add r11, %1 +%endmacro + +; Usage: commit_call_frame +%macro call_frame_commit 0 + add r11, 15 + and r11, ~15 + ; Aligned the total size up to the nearest 16 bytes + + sub rsp, r11 ; Allocate the final, aligned block on the stack + mov [rsp], r11 ; Store the total size at the bottom of the frame we just created +%endmacro + +; Usage: end_call +; Retrieves the size from the stack and deallocates the frame. +; This macro no longer depends on R11. +%macro call_frame_end 0 + mov r11, [rsp] ; Retrieve the total size from the bottom of our frame + add rsp, r11 ; Deallocate the entire frame +%endmacro + ;endregion Memory ;region Math @@ -365,28 +396,92 @@ str8_to_cstr_capped: %define MS_FILE_SHARE_READ 0x00000001 %define MS_GENERIC_READ 0x80000000 %define MS_OPEN_EXISTING 3 -%define MS_STD_OUTPUT_HANDLE 11 +%define MS_STD_OUTPUT_HANDLE -11 %define wapi_shadow_space 32 ; kernel32.lib +; Process API +extern CloseHandle +extern ExitProcess +extern GetLastError +; File API +extern CreateFileA +extern GetFileSizeEx +extern ReadFile +extern WriteFileA ; Console IO extern GetStdHandle extern WriteConsoleA -; File API -extern CloseHandle -extern CreateFileA -extern GetFileSizeEx -extern GetLastError -extern ReadFile -extern WriteFileA -; Process API -extern ExitProcess struc wapi_ctbl .shadow: resb 32 ; 32 bytes for RCX, RDX, R8, R9 endstruc +; rcx: hObject +struc CloseHandle_ctbl + .shadow: resq 4 +endstruc + +; rcx: uExitCode +struc ExitProcess_ctbl + .shadow: resq 4 +endstruc + +; no args +struc GetLastError_ctbl + .shadow: resq 4 +endstruc + +; rcx: lpFileName +; rdx: dwDesiredAccess +; r8: dwShareMode +; r9: lpSecurityAttributes +; s1: dwCreationDisposition +; s2: dwFlagsAndAttributes +; s3: hTemplateFile +; NOTE: Even though the first two are DWORDs, on the stack they each +; occupy a full 8-byte slot in the x64 ABI. +struc CreateFileA_ctbl + .shadow: resb 32 + .dwCreationDisposition: resb 8 + .dwFlagsAndAttributes: resb 8 + .hTemplateFile: resb 8 +endstruc + +; rcx: hFile +; rdx: lpFileSize +struc GetFileSizeEx_ctbl + .shadow: resq 4 +endstruc + +; rcx: hFile +; rdx: lpBuffer +; r8: nNumberOfBytesToRead +; r9: lpNumberOfBytesRead +; s1: lpOverlapped +struc ReadFile_ctbl + .shadow: resq 4 + .lpOverlapped: resq 1 + ._pad: resq 1 ; 8 bytes padding for 16-byte stack alignment +endstruc + +; rcx: hFile +; rdx: lpBuffer +; r8: nNumberOfBytesToWrite +; r9: lpNumberOfBytesWritten +; s1: lpOverlapped +struc WriteFileA_ctbl + .shadow: resq 4 + .lpOverlapped: resq 1 + ._pad: resq 1 ; 8 bytes padding for 16-byte stack alignment +endstruc + +struc FileOpInfo + .content: resb Slice_Byte_size +endstruc + +; rcx: nStdHandle struc GetStdHandle_ctbl .shadow: resb 32 endstruc @@ -400,53 +495,18 @@ struc WriteConsoleA_ctbl .shadow: resq 4 .lpReserved: resq 1 .lpNumberOfCharsWritten: resq 1 - ._pad_: resq 1 -endstruc - -struc CloseHandle_ctbl - .shadow: resq 4 - ; . -endstruc - -; r8: dwShareMode -; r9: lpSecurityAttributes -; s1: dwCreationDisposition -; s2: dwFlagsAndAttributes -; s3: hTemplateFile -; NOTE: Even though the first two are DWORDs, on the stack they each -; occupy a full 8-byte slot in the x64 ABI. -struc CreateFileA_ctbl - .shadow: resb 32 - .dwCreationDisposition: resb 8 - .dwFlagsAndAttributes: resb 8 - .hTemplateFile: resb 8 -endstruc - -; rcx: hfile -; rdx: lpbuffer -; r8: nNumberOfBytesToWrite -; r9: lpNumberOfBytesWritten -; s1: lpOverlapped -struc WriteFileA_ctbl - .shadow: resb 32 - .lpOverlapped: resb 8 endstruc section .data std_out_hndl dq 0 ;endregion WinAPI -struc FileOpInfo - .content: resb Slice_Byte_size -endstruc - ;region file_read_contents - +%push proc_scope ; Reg allocation: ; result: rcounter = [FileOpInfo] ; path: Slice_Str8 = { .ptr = rdata, .len = r8 } ; backing: r9 = [Slice_Byte] -%push proc_scope %define result rcounter %define path_ptr rdata %define path_len r8 @@ -454,9 +514,12 @@ endstruc section .text file_read_contents: + ; validation assert_not_null result slice_assert backing slice_assert path_ptr, path_len + + ; save registers push rbase ; id_file push r12 ; result push r13 ; backing @@ -465,22 +528,23 @@ file_read_contents: mov r13, backing %define result r12 %define backing r13 + ; rcounter = str8_to_cstr_capped(path, slice_fmem(scratch)); ; We're using backing to store the cstr temporarily until ReadFile. call str8_to_cstr_capped ; (rdata, r8, r9) ; path_cstr = rcounter; path_len has will be discarded in the CreateFileA call %define path_cstr rcounter - wapi_shadow_space - ; rcounter = path_cstr + stack_push CreateFileA_ctbl_size ; call-frame CreateFileA { + ; rcounter = path_cstr mov rdata_32, MS_GENERIC_READ ; dwDesiredAccess = MS_GENERIC_READ mov r8_32, MS_FILE_SHARE_READ ; dwShareMode = MS_FILE_SHARE_READ xor r9, r9 ; lpSecurityAttributes = nullptr - mov dword [rstack_ptr + wapi_CreateFileA_dwCreationDisposition], MS_OPEN_EXISTING - mov dword [rstack_ptr + wapi_CreateFileA_dwFlagsAndAttributes ], MS_FILE_ATTRIBUTE_NORMAL - mov qword [rstack_ptr + wapi_CreateFileA_hTemplateFile ], nullptr - call CreateFileA - stack_pop + mov dword [rstack_ptr + CreateFileA_ctbl.dwCreationDisposition], MS_OPEN_EXISTING ; stack.ptr[.dwCreationDisposition] = MS_OPEN_EXISTING + mov dword [rstack_ptr + CreateFileA_ctbl.dwFlagsAndAttributes ], MS_FILE_ATTRIBUTE_NORMAL ; stack.ptr[.dwFlagsAndAttributes ] = MS_FILE_ATTRIBUTE_NORMAL + mov qword [rstack_ptr + CreateFileA_ctbl.hTemplateFile ], nullptr ; stack.ptr[.hTemplateFile ] = nullptr + call CreateFileA ; CreateFileA <- rcounter, rdata, r8, r9, stack + stack_pop ; } ; B32 open_failed = raccumulator == MS_INVALID_HANDLE_VALUE ; if (open_failed) goto %%.error_exit @@ -490,36 +554,35 @@ file_read_contents: mov rbase, raccumulator ; rbase = id_file %define id_file rbase - wapi_shadow_space - mov rcounter, id_file - lea rdata, [result + FileOpInfo.content + Slice_Byte.len]; lpFileSize = result.content.len - call GetFileSizeEx - stack_pop + stack_push GetFileSizeEx_ctbl_size ; call-frame GetFileSizeEx { + mov rcounter, id_file ; rcounter = id_file + lea rdata, [result + FileOpInfo.content + Slice_Byte.len] ; lpFileSize = result.content.len + call GetFileSizeEx ; GetFileSizeEx <- rcounter, rdata, stack + stack_pop ; } ; B32 not_enough_backing = result.content.len > backing.len - ; if (not_enough_backing) goto .error_exit - mov r8, [backing + Slice_Byte.len] - mov r9, [result + FileOpInfo.content + Slice_Byte.len] - assert_cmp jle, r9, r8 - jg .error_close_handle + ; if (not_enough_backing) goto .error_close_handle + mov r8, [backing + Slice_Byte.len] ; r8 = backing.len + mov r9, [result + FileOpInfo.content + Slice_Byte.len] ; r9 = result.content.len + assert_cmp jle, r9, r8 ; r9 <= r8 + jg .error_close_handle ; if (flagged greater) goto .error_close_handle ; MS_BOOL get_size_failed = ! raccumulator ; if (get_size_failed) goto .error_exit - assert_cmp jne, raccumulator, false - je .error_close_handle + assert_cmp jne, raccumulator, false ; raccumulator != false + je .error_close_handle ; if (flagged equal) goto .error_close_handle - ; push r14 ; file_size %define file_size r14d mov r14d, r9d - wapi_shadow_space + stack_push ReadFile_ctbl_size ; call-frame ReadFile { mov rcounter, id_file ; hfile: rcounter = rbase mov rdata, [backing + Slice_Byte.ptr ] ; lpBuffer: rdata = backing.ptr mov r8_32, file_size ; nNumberOfBytesToRead: r8_32 = file_size lea r9, [result + FileOpInfo.content + Slice_Byte.len] ; lpNumberOfBytesRead: r9 = & result.content.len - mov qword [rstack_ptr + wapi_ReadFile_lpOverlapped], 0 ; lpOverlapped: nullptr - call ReadFile - stack_pop + mov qword [rstack_ptr + ReadFile_ctbl.lpOverlapped], 0 ; lpOverlapped: nullptr + call ReadFile ; ReadFile <- rcounter, rata, r8, r9, stack + stack_pop ; } ; B32 read_failed = ! read_result ; if (read_failed) goto .error_exit @@ -532,22 +595,21 @@ file_read_contents: jne .error_close_handle ; CloseHandle(id_file) - wapi_shadow_space - mov rcounter, rbase - call CloseHandle - stack_pop + stack_push CloseHandle_ctbl_size ; call-frame CloseHandle { + mov rcounter, id_file ; rcounter = id_file (rbase) + call CloseHandle ; CloseHandle <- rcounter, stack + stack_pop ; } ; reslt.content.ptr = raccumulator - mov raccumulator, [backing + Slice_Byte.ptr] - mov [result + FileOpInfo.content + Slice_Byte.ptr], raccumulator - jmp .cleanup + mov raccumulator, [backing + Slice_Byte.ptr] ; raccumulator = backing.ptr + mov [result + FileOpInfo.content + Slice_Byte.ptr], raccumulator ; result.content.ptr = raccumulator + jmp .cleanup ; goto .cleanup .error_close_handle: - ; CloseHandle(id_file) - wapi_shadow_space - mov rcounter, rbase - call CloseHandle - stack_pop + stack_push CloseHandle_ctbl_size ; call-frame CloseHandle { + mov rcounter, rbase ; rcounter = id_file (rbase) + call CloseHandle ; CloseHandle <- rcounter, stack + stack_pop ; } .error_exit: ; result = {} @@ -559,8 +621,8 @@ file_read_contents: pop backing pop result pop id_file + ; restore registers ret -%pop proc_scope section .bss ; local_persist raw_scratch : [64 * kilo]byte @@ -573,12 +635,13 @@ section .data at Slice_Byte.ptr, dq file_read_contents.raw_scratch at Slice_Byte.len, dq 64 * kilo iend +%pop proc_scope ;endregion file_read_contents section .text global main +%push proc_scope main: - %push proc_scope stack_push GetStdHandle_ctbl_size ; call-frame GetStdHandle { mov rcounter_32, -MS_STD_OUTPUT_HANDLE ; rcounter.32 = -MS_STD_OUTPUT_HANDLE call GetStdHandle ; GetStdHandle <- rcounter, stack @@ -586,10 +649,11 @@ global main stack_pop ; } ; dbg_wipe_gprs - %push calling - %assign stack_offset 0 - stack_slice Slice_Byte, local_backing ; call-frame file_read_contents { - stack_push stack_offset ; stack local_backing : Slice_byte + %push calling + call_frame + %define local_backing rsp + Slice_Byte_size + call_frame_alloc Slice_Byte ; stack local_backing : Slice_byte + call_frame_commit ; call-frame file_read_contents { mov qword [local_backing + Slice_Byte.ptr], read_mem ; local_backing.ptr = read_mem.ptr mov qword [local_backing + Slice_Byte.len], Mem_128k_size ; local_backing.len = Mem_128k_size lea rcounter, file ; rcounter = file.ptr @@ -597,24 +661,24 @@ global main mov r8, [path_hello_files_asm + Str8.len] ; r8 = path_hello_files.len lea r9, [local_backing] ; r9 = & local_backing call file_read_contents ; read_file_contents <- rcounter, rdata, r8, r9, stack - stack_pop ; } + call_frame_end ; } %pop calling - stack_push WriteConsoleA_ctbl_size ; call-frame WriteConsoleA { - mov rcounter, [std_out_hndl] ; rcounter = std_out_hndl - lea rdata, [file + FileOpInfo.content + Slice_Byte.ptr] ; rdata = file.content.ptr - mov r8_32, [file + FileOpInfo.content + Slice_Byte.len] ; r8 = file.content.len - lea r9, [rstack_ptr + WriteFileA_ctbl.lpNumberOfBytesWritten] ; r9 = & stack.ptr[WriteFileA.ctbl.lpNumberOfBytesWritten] - mov qword [rstack_ptr + WriteFileA_ctbl.lpReserved], nullptr ; stack.ptr[.ctbl.lpRserved] = nullptr - call WriteConsoleA ; WriteConsoleA <- rcounter, rdata, r9, stack - stack_pop ; } + stack_push WriteConsoleA_ctbl_size ; call-frame WriteConsoleA { + mov rcounter, [std_out_hndl] ; rcounter = std_out_hndl + lea rdata, [file + FileOpInfo.content + Slice_Byte.ptr] ; rdata = file.content.ptr + mov r8_32, [file + FileOpInfo.content + Slice_Byte.len] ; r8 = file.content.len + lea r9, [rstack_ptr + WriteConsoleA_ctbl.lpNumberOfCharsWritten] ; r9 = & stack.ptr[WriteFileA.ctbl.lpNumberOfCharsWritten] + mov qword [rstack_ptr + WriteConsoleA_ctbl.lpReserved], nullptr ; stack.ptr[.ctbl.lpRserved] = nullptr + call WriteConsoleA ; WriteConsoleA <- rcounter, rdata, r9, stack + stack_pop ; } ; Exit program stack_push ExitProcess_ctbl_size ; call-frame ExitProcess { xor ecx, ecx ; ecx = 0 call ExitProcess ; ExitProcess <- rcx, stack - ret ; } - %pop proc_scope + ret ; } // Technically doesn't occur but here for "correctness" +%pop proc_scope section .bss read_mem: resb Mem_128k_size ; internal global read_mem: Mem_128k diff --git a/code/asm/hello_files.proj b/code/asm/hello_files.proj index 2857c5e..5bcc434 100644 --- a/code/asm/hello_files.proj +++ b/code/asm/hello_files.proj @@ -1,6 +1,8 @@ // raddbg 0.9.18 project file recent_file: path: "hello_files.asm" +recent_file: path: "../../build/hello_files.unit.asm" +recent_file: path: "../../asm_dip/code/asm/hello_files.asm" recent_file: path: "../../../code/asm/hello_files.asm" recent_file: path: "../../../WATL_Exercise/c/watl.v0.msvc.c" recent_file: path: "asm/hello_files.asm" @@ -69,8 +71,8 @@ target: target: { executable: "../../build/hello_files.exe" - working_directory: ../../ enabled: 1 + working_directory: "../../../asm_dip" } breakpoint: { @@ -115,7 +117,7 @@ breakpoint: breakpoint: { source_location: "hello_files.asm:443:1" - hit_count: 1 + hit_count: 0 } breakpoint: { diff --git a/scripts/build_hello_files.ps1 b/scripts/build_hello_files.ps1 index 74983ab..99fb5c8 100644 --- a/scripts/build_hello_files.ps1 +++ b/scripts/build_hello_files.ps1 @@ -13,7 +13,8 @@ if ((test-path $path_build) -eq $false) { $unit_name = 'hello_files' -$unit = join-path $path_asm "$unit_name.asm" +$src = join-path $path_asm "$unit_name.asm" +$unit = join-path $path_build "$unit_name.unit.asm" $listing = join-path $path_build "$unit_name.asm.list" $link_obj = join-path $path_build "$unit_name.o" $map = join-path $path_build "$unit_name.map" @@ -36,6 +37,7 @@ $f_debug_fmt_win64 = '-g cv8' $f_dmacro = '-Dmacro=' $f_Ipath = '-Ipath ' $f_listing = '-l' +$f_listing_plus = '-L+' $f_preprocess_only = '-E' $f_optimize_none = '-O0' $f_optimize_min = '-O1' @@ -43,16 +45,29 @@ $f_optimize_multi = '-Ox' $f_optimize_multi_disp = '-Ov' $f_outfile = '-o ' $f_warnings_as_errors = '-Werror' -$args = @( - $unit, + +# $nargs = @( +# $src, +# $f_preprocess_only, +# $f_optimize_none, +# ($f_outfile + $unit) +# ) +# write-host 'Preprocessing' +# $nargs | ForEach-Object { Write-Host $_ } +# & $nasm $nargs + +$nargs = @( + $src, $f_optimize_none, $f_bin_fmt_win64, $f_debug_fmt_win64, + $f_listing_plus, ($f_listing + $listing), ($f_outfile + $link_obj) ) write-host 'Assembling' -& $nasm $args +$nargs | ForEach-Object { Write-Host "`t$_" } +& $nasm $nargs $lib_kernel32 = 'kernel32.lib' $lib_msvcrt = 'msvcrt.lib' @@ -72,7 +87,7 @@ $link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS' $rad_debug = '/RAD_DEBUG' $rad_debug_name = '/RAD_DEBUG_NAME:' $rad_large_pages = '/RAD_LARGE_PAGES:' -$args = @( +$nargs = @( # $rad_debug, $link_nologo, ($link_debug + 'FULL'), @@ -90,16 +105,16 @@ $args = @( $link_obj ) write-host 'Linking' -& $link $args +$nargs | ForEach-Object { Write-Host "`t$_" } +& $link $nargs pop-location $rbin_out = '--out:' $rbin_dump = '--dump' write-host 'Dumping RDI' -$args = @($pdb, ($rbin_out + $rdi)) -& $radbin $args -$args = @($rbin_dump, $rdi) -$dump = & $radbin $args +$nargs = @($pdb, ($rbin_out + $rdi)) +& $radbin $nargs +$nargs = @($rbin_dump, $rdi) +$dump = & $radbin $nargs $dump > $rdi_listing - diff --git a/scripts/build_hello_nasm.ps1 b/scripts/build_hello_nasm.ps1 index 26931fa..0348ff4 100644 --- a/scripts/build_hello_nasm.ps1 +++ b/scripts/build_hello_nasm.ps1 @@ -35,6 +35,7 @@ $f_debug_fmt_win64 = '-g cv8' $f_dmacro = '-Dmacro=' $f_Ipath = '-Ipath ' $f_listing = '-l' +$f_listing_plus = '-L+' $f_preprocess_only = '-E' $f_optimize_none = '-O0' $f_optimize_min = '-O1' @@ -42,15 +43,26 @@ $f_optimize_multi = '-Ox' $f_optimize_multi_disp = '-Ov' $f_outfile = '-o ' $f_warnings_as_errors = '-Werror' + +write-host 'Preprocessing' $args = @( $unit, + $f_preprocess_only, + $f_optimize_none, + ($f_listing + $listing), + $f_listing_plus +) +& $nasm $args + +write-host 'Assembling' +$args = @( + $unit, + $f_preprocess_only, $f_optimize_none, $f_bin_fmt_win64, $f_debug_fmt_win64, - ($f_listing + $listing), ($f_outfile + $link_obj) ) -write-host 'Assembling' & $nasm $args $lib_kernel32 = 'kernel32.lib'