【问题标题】:Arguments to function not getting passed in correctly MASM没有正确传递 MASM 函数的参数
【发布时间】:2017-07-05 15:18:16
【问题描述】:

我正在学习 MASM,但无法让这个简单的代码工作。我没有得到传递给调用的值,我不知道发生了什么。我试过推 2,推 2,叫 pow。结果相同。 EAX 和 EDX 看起来像垃圾或者内存地址。

线程 0x1544 已退出,代码为 -1073741510 (0xc000013a)。 线程 0xd8 已退出,代码为 -1073741510 (0xc000013a)。 线程 0x898 已退出,代码为 -1073741510 (0xc000013a)。 线程 0x21c4 已退出,代码为 -1073741510 (0xc000013a)。 程序“[2296] AssemblyTutorial.exe”已退出,代码为 -1073741510 (0xc000013a)。

这只是意味着我关闭了控制台窗口,但是为什么有 4 个线程?

.386
.model flat,stdcall
option casemap:none

include windows.inc

include masm32.inc
includelib masm32.lib

include kernel32.inc
includelib kernel32.lib

include user32.inc
includelib user32.lib


.code

;requires power > 0 for correct answers
pow proc x:DWORD, power:DWORD

    ;THESE TWO MOVES RIGHT HERE ARE NOT WORKING AS EXPECTED
    ;I PUSH 2 ONTO STACK BUT WHEN I LOOK AT REGISTER VALUES
    ;IN DEBUG THEY ARE INCORRECT/GARBAGE
    mov eax, x      ;eax has x
    mov edx, power  ;edx has power
    mov ecx, eax    ;ecx will be my constant base

start_power:
    cmp edx, 1      ;is power 1?
    je end_power    ;then jump to end
    imul eax, ecx   ;else mul result/x with x
    dec edx         ;power--
    jmp start_power ;jump back to beginning of loop

end_power:
    ret             ;return answer eax

pow endp


start:

invoke pow, 2, 2 ;eax should hold 4 after this
invoke ExitProcess, eax ;program should exit with code eax, ez way to print result

end start

【问题讨论】:

  • 我必须自己用stdcall管理堆栈指针和基指针吗?我的印象是 masm proc 会为你做到这一点。
  • 您可以编辑自己的帖子,而不是添加 cmets 来详细说明您的问题。
  • IMUL 总是将结果放入 EDX?我也不明白 EDX:EAX 是什么意思。像这样在中间加一个冒号是什么意思?
  • "IMUL指令的三种形式的相似之处在于乘积的长度被计算为操作数长度的两倍。使用单操作数形式,乘积被准确地存储在目标中. 然而,对于二和三操作数形式,结果在存储到目标寄存器之前会被截断到目标的长度。由于这种截断,应测试 CF 或 OF 标志以确保没有显着位丢失。”所以结果将被截断而不跨越多个寄存器。结果将在 EAX 中。没有?
  • 您的权利。道歉。我的假设是错误的。

标签: assembly x86 masm masm32


【解决方案1】:

是的,cdecl和stdcall的区别在于前者是caller-clean,后者是callee-clean。 (另见Raymond Chen's series on calling conventions in Windows

问题是,您的pow 过程没有遵循 stdcall 约定,因为它没有清理堆栈。您需要在ret 指令中指定要弹出的字节数。在这种情况下,就是ret 8

或者,您可以创建函数 cdecl,在这种情况下,调用者负责清理堆栈,MASM 可以自动生成此代码作为 INVOKE 指令的一部分。

为什么有 4 个线程?

Windows 出于各种原因启动后台线程。这些没什么好担心的。如果您进一步调查它们,您可能会发现它们是由线程池工作线程(ntdll.dll 中的TppWorkerThread)启动的。


对于它的价值,pow 函数可以更有效地写成:

pow PROC x:DWORD, power:DWORD
    ; Load parameters into registers
    mov  eax, x
    mov  edx, power
    mov  ecx, eax

    ; Decrement 'power' by 1 and bail out if we're done.
    dec  edx
    jz   Finished

    ; The main loop.
CalculatePow:
    imul eax, ecx
    dec  edx
    jnz  CalculatePow

Finished:
    ret  8        ; assuming this function is STDCALL
pow ENDP

【讨论】:

  • 非常感谢!那个帖子里有很多好信息!我不知道 dec 设置了这样的零标志。好东西。我也不知道 c 代表 cdecl 哈哈。 invoke 和 ret 是否会更改它们的行为以匹配调用约定?我从没见过有人清理堆栈、设置基指针或 MASM 代码中的任何好东西。
  • @user 是的,cdecl 中的“c”指的是 C 语言,因为这是 C 中的标准调用约定。Windows 没有使用它,因为您可以通过使用callee 清理堆栈,这是 stdcall 所做的。 “std”表示 Windows 中的标准。它基于 16 位 Windows 的早期调用约定,称为“pascal”,因为这是 Pascal 语言使用的。我不知道您一直在查看什么 MASM 代码,但是我看到的 所有 代码都会清理堆栈。再说一次,我很少使用invoke,因为它不能移植到其他汇编程序。
  • 是的,INVOKE 根据调用约定自动处理推送和弹出参数。 但是它不会影响函数实现中的RET 指令,因此stdcall 函数仍然需要通过执行ret x 自己清理堆栈,其中x 是要执行的字节数流行音乐。要弹出的字节数是RET可选 参数。它用于 stdcall 函数,但不用于 cdecl 函数。使用 cdecl 函数,INVOKE 将处理堆栈清理,因为清理是在调用方完成的。
  • 嗯。也许我错了,INVOKE 确实 改变了代码输出。我不知道。 INVOKE在线没有好的文档;你必须回到old MASM 6 manuals。那里的示例仅显示 INVOKE 与 cdecl 函数一起使用,确实表明,如果您使用带有 PROC 指令的自动序言/尾声代码,并且您使用 RET 指令如果没有操作数,那么汇编器将自动附加正确的字节数。所以这可能是它的原因,而不是INVOKE
  • 参见第 4 页。 159:“当遇到没有操作数的 RET 指令时,汇编程序生成标准结尾代码。如果 RET 有一个非零操作数,它不会生成结尾代码。要禁止生成标准结尾代码,请使用带或不带的 RETN 或 RETF一个操作数,或者使用 RET 0。” 老实说,我尽量避免在汇编中使用过于复杂的宏。就我而言,它们只是使代码的可读性降低,因为幕后发生了很多魔术。如果我愿意,我会用 C 编程!
【解决方案2】:

将调用约定更改为 c 并且它可以工作,但我不知道为什么。如果有人能解释一下那就太好了。

使用 stdcall 调用约定,被调用者清理堆栈......这就是我所知道的。肯定还有更多。从 pow 函数返回后,堆栈指针可以在任何位置继续,事情应该可以正常工作吧?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 2021-09-03
    • 1970-01-01
    相关资源
    最近更新 更多