Author Topic: Asmc source and binaries  (Read 3941 times)

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #30 on: April 07, 2017, 08:12:51 AM »
Yep, that failed  :biggrin:

Updated winuser.inc..

New source: Note that align is only needed for xmm/ymmword locals.
Code: [Select]
include windows.inc

.win64: rbp
.code

start proc

    mov rbx,GetModuleHandle(0)
    ExitProcess(WinMain(rbx, 0, GetCommandLine(), SW_SHOWDEFAULT))

start endp

WinMain PROC hInstance: HINSTANCE,
hPrevInstance: HINSTANCE,
     lpCmdLine: LPSTR,
      nShowCmd: SINT

    LOCAL   wc:WNDCLASSEX
    LOCAL   msg:MSG
    LOCAL   hwnd:QWORD

    mov     wc.cbSize,SIZEOF WNDCLASSEX
    mov     wc.style,CS_HREDRAW or CS_VREDRAW
    mov     rax,OFFSET WndProc
    mov     wc.lpfnWndProc,rax

    xor     rax,rax
    mov     wc.cbClsExtra,eax
    mov     wc.cbWndExtra,eax
    mov     wc.hInstance,rcx
    mov     wc.hbrBackground,COLOR_WINDOW+1
    mov     wc.lpszMenuName,rax

    lea     rax,@CStr("WndClass")
    mov     wc.lpszClassName,rax
    mov     wc.hIcon,LoadIcon(0,IDI_APPLICATION)
    mov     wc.hIconSm,rax
    mov     wc.hCursor,LoadCursor(0,IDC_ARROW)
    RegisterClassEx(ADDR wc)

    mov     eax,CW_USEDEFAULT
    mov     hwnd,CreateWindowEx(0,"WndClass","Window",WS_OVERLAPPEDWINDOW,
eax,eax,eax,eax,0,0,hInstance,0)

    ShowWindow(hwnd,SW_SHOWNORMAL)
    UpdateWindow(hwnd)

    .while GetMessage(ADDR msg,0,0,0)

TranslateMessage(ADDR msg)
DispatchMessage(ADDR msg)
    .endw

    mov rax,msg.wParam
    ret

WinMain ENDP

WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .if edx == WM_DESTROY

PostQuitMessage(0)
xor rax,rax
    .else
DefWindowProc(hWnd,uMsg,wParam,lParam)
    .endif
    ret

WndProc ENDP

END

The typing reduce the source with around 50 byte, however the linker increase the size of the exe to 4096.

Using rsp also works, but this extends the code a bit:
Code: [Select]
.win64: rsp save
00000208                    *   _TEXT ends
.win64: rbp
000001CF                    *   _TEXT ends

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #31 on: April 08, 2017, 01:43:00 AM »
The Windows 64 ABI may seem complicated and messy, at least in assembly, but by using a standard stack-frame it basically behaves just like a STDCALL function.

There is however the possibility to simplify the mess as compilers do, but it becomes an open question then how far you have to stretch this before you end up compiling as oppose to just assemble the source. I tend to support the latter in this case, using the safe settings for all and apply manual optimization later.

The two normal optimization options are:
- if the function don't make any calls: skip the frame
- if the function can use the regs instead of the arguments: skip save.

Both of these could have been compiled I guess, but as for now this have to be done manually.

Example:

The last proc in the above sample may be optimized as follow:
Code: [Select]
.win64: rsp nosave

WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .if edx == WM_DESTROY

PostQuitMessage(0)
xor rax,rax
    .else
DefWindowProc(rcx, edx, r8, r9)
    .endif
    ret

WndProc ENDP

Standard version:
Code: [Select]
WndProc PROC
        mov     qword ptr [rsp+8H], rcx                 ; 0185 _ 48: 89. 4C 24, 08
        mov     qword ptr [rsp+10H], rdx                ; 018A _ 48: 89. 54 24, 10
        mov     qword ptr [rsp+18H], r8                 ; 018F _ 4C: 89. 44 24, 18
        mov     qword ptr [rsp+20H], r9                 ; 0194 _ 4C: 89. 4C 24, 20
        push    rbp                                     ; 0199 _ 55
        mov     rbp, rsp                                ; 019A _ 48: 8B. EC
        sub     rsp, 32                                 ; 019D _ 48: 83. EC, 20
        cmp     edx, 2                                  ; 01A1 _ 83. FA, 02
        jnz     ?_003                                   ; 01A4 _ 75, 0F
        mov     ecx, 0                                  ; 01A6 _ B9, 00000000
        call    PostQuitMessage                         ; 01AB _ E8, 00000000(rel)
        xor     rax, rax                                ; 01B0 _ 48: 33. C0
        jmp     ?_004                                   ; 01B3 _ EB, 14

?_003:  mov     rcx, qword ptr [rbp+10H]                ; 01B5 _ 48: 8B. 4D, 10
        mov     edx, dword ptr [rbp+18H]                ; 01B9 _ 8B. 55, 18
        mov     r8, qword ptr [rbp+20H]                 ; 01BC _ 4C: 8B. 45, 20
        mov     r9, qword ptr [rbp+28H]                 ; 01C0 _ 4C: 8B. 4D, 28
        call    DefWindowProcA                          ; 01C4 _ E8, 00000000(rel)
?_004:  add     rsp, 32                                 ; 01C9 _ 48: 83. C4, 20
        leave                                           ; 01CD _ C9
        ret                                             ; 01CE _ C3
WndProc ENDP

Optimized version:
Code: [Select]
WndProc PROC
        sub     rsp, 40                                 ; 0185 _ 48: 83. EC, 28
        cmp     edx, 2                                  ; 0189 _ 83. FA, 02
        jnz     ?_003                                   ; 018C _ 75, 0F
        mov     ecx, 0                                  ; 018E _ B9, 00000000
        call    PostQuitMessage                         ; 0193 _ E8, 00000000(rel)
        xor     rax, rax                                ; 0198 _ 48: 33. C0
        jmp     ?_004                                   ; 019B _ EB, 05

?_003:  call    DefWindowProcA                          ; 019D _ E8, 00000000(rel)
?_004:  add     rsp, 40                                 ; 01A2 _ 48: 83. C4, 28
        ret                                             ; 01A6 _ C3
WndProc ENDP

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #32 on: June 06, 2017, 10:33:44 AM »
Hi nidud,

Regarding 64-bit coding, it's easy to create .def files and import libraries from DLLs :

Creating include files and import libraries for ml64

Hi Vortex,

I tested Dll2definc.exe/def2lib64.exe but the LIB file only include 32-bit imports. The problem is that Dll2definc is a 32-bit EXE, so LoadLibrary() will load the 32-bit version of kernel32.dll.

Code: [Select]
include stdio.inc
include winnt.inc
include winbase.inc

.code

main proc argc:dword, argv:ptr
local dll
    .if argc == 2
mov esi,argv
.if LoadLibrary([esi+4])
    mov dll,eax
    mov edi,eax
    add edi,[eax+0x3C]
    mov edi,[edi].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress
    add edi,eax
    mov ebx,[edi+0x18]
    mov esi,[edi+0x20]
    add esi,eax
    .while ebx
mov eax,[esi]
add eax,dll
printf("%s\n", eax)
add esi,4
dec ebx
    .endw
    FreeLibrary(dll)
.endif
    .endif
    xor eax,eax
    ret
main endp

    end

Some missing functions:
- RtlUnwindEx
- RtlVirtualUnwind

So here's a 64-bit version:
Code: [Select]
;
; Build: asmc -pe -D__PE__ -D_WIN64 dlldef.asm
;
include string.inc
include stdio.inc
include stdlib.inc
include winbase.inc
include tchar.inc

    .code

main proc
    .if rcx == 2
mov rdi,[rdx+8]
        .if LoadLibrary(rdi)
            mov r12,rax
            mov rbx,strcpy(strrchr(rdi,'.'),".def")
            .if fopen(rdi,"wt")
                mov r13,rax
                mov byte ptr [rbx],0
                fprintf(r13,"LIBRARY %s\nEXPORTS\n",rdi)
mov eax,[r12+0x3C]
mov eax,[r12+rax].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress
mov ebx,[r12+rax+0x18]
mov esi,[r12+rax+0x20]
                add rsi,r12
                .while ebx
                    lodsd
    fprintf(r13,"\"%s\"\n",addr [rax+r12])
                    dec ebx
                .endw
                fclose(r13)
            .endif
            FreeLibrary(r12)
        .endif
    .else
        printf("\nUsage: DLLDEF <dllname>.dll\n\n")
    .endif
    xor eax,eax
    ret
main endp

    end _tstart

I only tested with kernel32 using def2lib64 and that seems to work.
« Last Edit: June 06, 2017, 01:31:13 PM by nidud »

Vortex

  • Member
  • *****
  • Posts: 1711
Re: Asmc source and binaries
« Reply #33 on: June 09, 2017, 03:57:31 AM »
Hi nidud,

Thanks for your tests and the tool. My assumption considering that both of the 32-bit and 64-bit versions of a DLL are exporting the same functions was wrong.

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #34 on: June 09, 2017, 09:19:18 AM »
My assumption considering that both of the 32-bit and 64-bit versions of a DLL are exporting the same functions was wrong.

I sort of assumed the same.

The problem here is that LIBW is capable of creating a working import record from the command line but fail to do so from a .dll file. Asmc also use the same code for the /pe switch. This means that the problem is fixable within the source for LIBW, but I haven't looked into this yet.

Well, now we know why but the lib creation from the list produced works thought so no changes is needed there. I updated the makefiles for building the import libraries so that should work now.

https://github.com/nidud/asmc/blob/master/lib/

coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #35 on: June 15, 2017, 05:32:08 PM »
The Windows 64 ABI may seem complicated and messy, at least in assembly, but by using a standard stack-frame it basically behaves just like a STDCALL function.
No, it is not. It looks messy because people don't have a clear understanding of the ABI itself. If you read the spec carefully, the ABI requires only 3 general criteria;

1. allocate shadow space
2. align the stack to 16 (everybody must ensure that this is done in their function bodies before calling out others)
3. XMMs for floating point, GPRs for integers (rcx,rdx...)

That's it. Everything else is not considered part of the ABI spec. For example, saving the volatile registers (rcx... r9) is never part of the ABI. It's up to the user's code whether to save them or not. YMM registers are not part of the ABI either. The code below shows your confusion in implementing the ABI. This my friend, indeed looks messy, because your're attempting to implement BOTH legacy STDCALL and FASTCALL on top of each other. It has nothing to do with optimized vs standard code.

Quote from: nidud
Standard version:
Code: [Select]
WndProc PROC
        mov     qword ptr [rsp+8H], rcx                 ; 0185 _ 48: 89. 4C 24, 08
        mov     qword ptr [rsp+10H], rdx                ; 018A _ 48: 89. 54 24, 10
        mov     qword ptr [rsp+18H], r8                 ; 018F _ 4C: 89. 44 24, 18   ;saving volatile registers part of the ABI?
        mov     qword ptr [rsp+20H], r9                 ; 0194 _ 4C: 89. 4C 24, 20
        push    rbp                                     ; 0199 _ 55                                   ;STDCALL?
        mov     rbp, rsp                                ; 019A _ 48: 8B. EC
        sub     rsp, 32                                 ; 019D _ 48: 83. EC, 20
        cmp     edx, 2                                  ; 01A1 _ 83. FA, 02
        jnz     ?_003                                   ; 01A4 _ 75, 0F
        mov     ecx, 0                                  ; 01A6 _ B9, 00000000
        call    PostQuitMessage                         ; 01AB _ E8, 00000000(rel)
        xor     rax, rax                                ; 01B0 _ 48: 33. C0
        jmp     ?_004                                   ; 01B3 _ EB, 14

?_003:  mov     rcx, qword ptr [rbp+10H]                ; 01B5 _ 48: 8B. 4D, 10
        mov     edx, dword ptr [rbp+18H]                ; 01B9 _ 8B. 55, 18
        mov     r8, qword ptr [rbp+20H]                 ; 01BC _ 4C: 8B. 45, 20
        mov     r9, qword ptr [rbp+28H]                 ; 01C0 _ 4C: 8B. 4D, 28
        call    DefWindowProcA                          ; 01C4 _ E8, 00000000(rel)
?_004:  add     rsp, 32                                 ; 01C9 _ 48: 83. C4, 20
        leave                                           ; 01CD _ C9
        ret                                             ; 01CE _ C3
WndProc ENDP


nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #36 on: June 15, 2017, 10:05:53 PM »
No, it is not. It looks messy because people don't have a clear understanding of the ABI itself. If you read the spec carefully, the ABI requires only 3 general criteria;

1. allocate shadow space

So the shadow space is part of the Application binary interface then.

Quote
For example, saving the volatile registers (rcx... r9) is never part of the ABI.

The first four integer or pointer parameters are passed in the rcx, rdx, r8, and r9 registers. The caller reserves space on the stack for arguments passed in registers. This is what the shadow space is used for.

https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture

Quote
The code below shows your confusion in implementing the ABI. This my friend, indeed looks messy, because your're attempting to implement BOTH legacy STDCALL and FASTCALL on top of each other. It has nothing to do with optimized vs standard code.

Yes, the ABI may look a bit complicated, messy and confusing in assembly.

coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #37 on: June 15, 2017, 11:16:40 PM »
No, it is not. It looks messy because people don't have a clear understanding of the ABI itself. If you read the spec carefully, the ABI requires only 3 general criteria;

1. allocate shadow space

So the shadow space is part of the Application binary interface then.

Quote
For example, saving the volatile registers (rcx... r9) is never part of the ABI.

The first four integer or pointer parameters are passed in the rcx, rdx, r8, and r9 registers. The caller reserves space on the stack for arguments passed in registers. This is what the shadow space is used for.

https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture

Quote
The code below shows your confusion in implementing the ABI. This my friend, indeed looks messy, because your're attempting to implement BOTH legacy STDCALL and FASTCALL on top of each other. It has nothing to do with optimized vs standard code.

Yes, the ABI may look a bit complicated, messy and confusing in assembly.
I don't know. It's up to you to interpret my statements and suggestions. Maybe my terminologies are wrong, but your codes look funnier. If you think that stacking both fastcall and stdcall at the same entry point is a 'standard' code and practice, then enjoy it.

 

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #38 on: June 16, 2017, 12:33:15 AM »
I don't know. It's up to you to interpret my statements and suggestions. Maybe my terminologies are wrong, but your codes look funnier.

Well, the sample you refer to is not "code" but the disassembled output from the object created by the assembler. The tool used is Agner Fog's objconv.exe.
Code: [Select]
Object file converter version 2.38 for x86 and x86-64 platforms.
Copyright (c) 2014 by Agner Fog. Gnu General Public License.

The original code was written by Vortext in Reply #28, and I used this as a test case for the windows.inc project in Reply #30.

Quote
If you think that stacking both fastcall and stdcall at the same entry point is a 'standard' code and practice, then enjoy it.

That may be confusing if your not familiar with the STDCALL calling convention. The 64-bit ABI is not "pure" FASTCALL but a combination of passing some arguments as registers and the rest on the stack. In addition to this stack is allocated (by the caller) for the arguments passed in registers.

The point of comparing STDCALL/FASTCALL has to do with the PROC and INVOKE directives and how this is handled by the assembler.

Code for Windows 32 ABI (stdcall)
Code: [Select]
WinMain PROC hInstance: HINSTANCE,
hPrevInstance: HINSTANCE,
     lpCmdLine: LPSTR,
      nShowCmd: SINT

strlen(lpCmdLine)

Code for Windows 64 ABI (fastcall)
Code: [Select]
WinMain PROC hInstance: HINSTANCE,
hPrevInstance: HINSTANCE,
     lpCmdLine: LPSTR,
      nShowCmd: SINT

strlen(lpCmdLine)

To see the code produced the object files are disassembled.

coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #39 on: June 16, 2017, 01:41:05 AM »
I don't understand your point, Nidud. I questioned the validity of what you called as 'standard' vs 'optimized'. There's no way that the first output can be called a standard of any kind. Standard against what? Saving the volatile registers has never been part of any 64-bit ABI 'standard'. And you're wrong about the shadow space being used to save the volatile registers because it is not. It's the responsibility of the users / callers to save them, hence you don't see them in the second output. Do you see the second output making attempts to save RCX to R9? No. So do the Win64 APIs. They don't save the registers for you because it's not part of the ABI requirement. You, the callers, do it yourself. It's not "optimized". It's how it should be in a tight 64-bit ABI environment.

So it comes down to this;

Code: [Select]
;align the stack (this is requirement)
sub rsp,020h   ;---> this is requirement
mov [rsp],rcx  ;---> these are NOT.
...
...
mov [rsp+...],r9
call aWin64API  ;---> then you call the function. Not saving the volatile registers inside it.

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #40 on: June 16, 2017, 02:23:21 AM »
I don't understand your point, Nidud.

The point is that this is not a help-desk for confused coders. If your confused (which you clearly are) about how the Windows 64 ABI works you should read up on the subject, ask Microsoft or post a question in the Campus.

coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #41 on: June 16, 2017, 02:38:32 AM »
The first four integer or pointer parameters are passed in the rcx, rdx, r8, and r9 registers. The caller reserves space on the stack for arguments passed in registers. This is what the shadow space is used for.

https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture

No, simpleton. I am talking with proof! If you're smart, you don't listen to anything that MS tells you. You didn't read hard enough. The shadow space is indeed required but to put the volatile registers (rcx-r9) in there is optional, and in many occasions, it is used as a spill area for a functions own code, corrupting what you've put in there. Here's one example, one attempt to preserve r8, as per 'Microsoft recommendation';

Code: [Select]
externdef Sleep:proc
externdef printf:proc
option casemap:none

dseg segment page 'DATA'
  isr8 db 'R8 preserved? = %llX',0ah,0
dseg ends

cseg segment para 'CODE'
main proc
        sub     rsp,8     ;align stack (requirement)

        mov     r8,0CCh   ;we'll see about it

        sub     rsp,20h   ;allocate space (requirement)
        mov     [rsp],rcx
        mov     [rsp+8],rdx
        mov     [rsp+16],r8
        mov     [rsp+24],r9

        mov     rcx,500   ;integer argument (requirement)
        call    Sleep     ;call a WinAPI

        mov     rcx,[rsp]
        mov     rdx,[rsp+8]
        mov     r8,[rsp+16]
        mov     r9,[rsp+24]
        add     rsp,20h

        sub     rsp,20h
        mov     rdx,r8    ;is r8 preserved by Sleep
        mov     rcx,offset isr8
        call    printf
        add     rsp,28h   ;restore all

        ret

main endp
cseg ends
end

The output shows that R8 (RSP+16) is corrupted. Here's the output.
Code: [Select]
D:\MASM\MYCODE>ml64 /c prog.asm
Microsoft (R) Macro Assembler (x64) Version 14.10.25019.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: prog.asm

D:\MASM\MYCODE>gcc -m64 prog.obj -s -o prog.exe

D:\MASM\MYCODE>prog
R8 preserved? = 0


coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #42 on: June 16, 2017, 02:47:26 AM »
I don't understand your point, Nidud.

The point is that this is not a help-desk for confused coders. If your confused (which you clearly are) about how the Windows 64 ABI works you should read up on the subject, ask Microsoft or post a question in the Campus.

One thing for sure, you're confusing the stack area for something else. Instead of dumping data there like everybody else do, you're stacking up two calling conventions there and call it "standard". Your "standard" is funny.

 

 

coder

  • Member
  • **
  • Posts: 103
Re: Asmc source and binaries
« Reply #43 on: June 19, 2017, 04:27:12 AM »
Hi Nidud. I hope you're not offended in any way from what I said. Keep up the good work!

nidud

  • Member
  • *****
  • Posts: 1386
    • https://github.com/nidud/asmc
Re: Asmc source and binaries
« Reply #44 on: June 19, 2017, 05:36:31 AM »

 :biggrin:

No problem coder but you may consider tone down your rhetoric a bit.

Do you understand now why the arguments passed in registers needs to be saved in WinMain() but not in WndProc() in the example above?