Search code examples
c++assemblyx86-64masm

Printf on masm x64 with multiple parameters


I need to create a function in asm (on Windows) that replicates the following behaviour:

_int64 q(_int64 a, _int64 b, _int64 c, _int64 d, _int64 e) {
     _int64 sum = a + b + c + d + e;
      printf("a = %I64d b = %I64d c = %I64d d = %I64d e = %I64d sum = %I64d\n", a, b, c, d, e, sum);
      return sum;
}

I know I need to allocate shadow space for printf and also that I need to store some parameters in the stack, because only the first 4 parameters are in registers (rcx, rdx, r8 and r9)

My problem comes with the string format and the stack management. My code so far looks like this:

.data   
string1 dq 'a = %I64d b = %I64d c = %I64d d = %I64d e = %I64d sum = %I64d', 10, 0       ; The printf format, "\n",'0'

.code
    public      q                               ;a in rcx, b in rdx, c in r8, d in r9, e in stack
    q:          push rbp
                mov rbp, rsp                    
                sub rsp, 32                     ;allocating shadow space for printf
                                                ;for calling printf, we need to have [string] in rcx, 
                                                ;a in rdx, b in r8, c in r9, d in stack1, e in stack2, and sum in stack3
                add rax, rcx                    ;first make the sum
                add rax, rdx                    
                add rax, r8
                add rax, r9
                mov rbx, [rbp + 8]              ;getting e from the stack
                add rax, rbx                    ;final add, in rax now is sum
                push rax                        ;changing parameters in registers (last 3 in stack)
                push rbx
                push r9
                mov r9, r8                      ;c in r9
                mov r8, rdx                     ;b in r8
                mov rdx, rcx                    ;a in rdx
                lea rcx, [string]               ;string in rcx
                call printf
                mov rsp, rbp                    ;back to previous pointer
                pop rbp                         ;release resources
                ret 0
    end

At this moment it does not compile, with an

error A2084: constant value too large

I don't know if I need to change the format or split it in 2, in that case I need to storage some other parameter in the stack, and then I am not very sure how to proceed...


Solution

  • If that error is on the line with the string, use db instead of dq: you don't want the 10, 0 elements padded to qwords even if it did accept the quoted part as a string the way NASM does.

    I think MASM allows quoted constants for db, so that should get it to assemble.


    And then we have multiple other bugs at runtime:

    Also, you need to sub rsp, 32to reserve shadow space after pushing 3 registers, if you want to restore them after. Otherwise those 24 bytes are the bottom of the shadow space the called function (printf) sees.

    But that's pointless because you're not restoring them after the call. So you violate the calling convention by clobbering rbx; It doesn't look like you're doing anything with them after the call, so just use a call-clobbered reg, or better calculate the value in the right arg-passing register in the first place.