【问题标题】:Printf on masm x64 with multiple parameters具有多个参数的 masm x64 上的 Printf
【发布时间】:2018-09-26 21:51:40
【问题描述】:

我需要在 asm(在 Windows 上)中创建一个复制以下行为的函数:

_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;
}

我知道我需要为 printf 分配影子空间,还需要在堆栈中存储一些参数,因为只有前 4 个参数在寄存器中(rcx、rdx、r8 和 r9)

我的问题在于字符串格式和堆栈管理。 到目前为止,我的代码如下所示:

.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

此时它没有编译,带有一个

错误 A2084:常数值太大

我不知道是否需要更改格式或将其拆分为2,这样我需要在堆栈中存储一些其他参数,然后我不太确定如何进行......

【问题讨论】:

  • 错误在哪一行?在弦上?使用db,您不希望将10, 0 元素填充到qwords,即使它确实像NASM 那样接受了这一点。
  • 另外,如果你想在之后恢复它们,你需要sub rsp, 32推送 3 个寄存器之后保留影子空间。否则,这 24 个字节是被调用函数 (printf) 看到的影子空间的底部。但这毫无意义,因为您不会在通话后恢复它们。因此,您通过破坏 rbx 违反了调用约定;使用不同的注册。此外,您还需要 call _printf,因为这就是 Windows 装饰 C 名称的方式。
  • @PeterCordes 是在字符串声明中......我有以下错误 A2006: undefined symbol : string

标签: c++ assembly x86-64 masm


【解决方案1】:

如果该错误与字符串一致,请使用db 而不是dq:您不希望将10, 0 元素填充到qwords,即使它确实接受了引用部分作为字符串的方式NASM 可以。

我认为 MASM 允许 db 引用常量,因此应该可以组装它。


然后我们在运行时还有多个其他错误:

另外,如果你想在之后恢复它们,你需要sub rsp, 32推送 3 个寄存器之后保留影子空间。否则,这 24 个字节是被调用函数 (printf) 看到的影子空间的底部。

但这没有意义,因为您不会在通话后恢复它们。所以你通过破坏rbx 违反了调用约定;看起来你在调用之后并没有对它们做任何事情,所以只需使用调用破坏的 reg,或者最好首先计算正确的 arg 传递寄存器中的值。

【讨论】:

  • 谢谢,现在它可以工作了,但是 e 和 sum 不能正常工作。不过,我不太明白最后一段:S。难道我还需要为 printf 分配影子空间才能使用它吗??
  • @MartaLobo:是的,您调用的任何函数都可以在返回地址上方超过 32 个字节。您需要确保这些字节不包含任何有价值的东西。现在它已经组装好了,使用调试器来找出你的代码在你单步执行时正在做什么。但我注意到您以 add rax, rcx 而不是 mov 开头,RAX 最初会保存您的调用者留下的任何垃圾。
  • 是的,我注意到了,我添加了几个异或来重置寄存器。现在我剩下的唯一问题是如何从堆栈中获取“e”并正确添加它......
  • @MartaLobo:您是否尝试过只查看 C 函数的优化编译器输出?将您的函数放在godbolt.org 上并使用x86-64 MSVC -Ox 编译它。不要忘记你自己的调用者在堆栈参数之前在返回地址上方留下的影子空间。
  • 非常感谢,那个网站非常有用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-10
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
相关资源
最近更新 更多