3 assembly x86-64 nasm calling-convention
我开始在 ubuntu linux 上使用 NASM 汇编器学习 x86_64 汇编编程。我遇到的问题之一是弄清楚操作神奇地使用了哪些寄存器。
我正在阅读的书有这样的代码示例:
mov rdi, fmt1
mov rsi, strng
mov rax, 0
call printf
; How am I supposed to know which registers are used by the call to printf?
; The libc printf function supports an arbitrary number of parameters.
; Clearly there aren't an unlimited number of registers in x86_64 so how does this work
; as the parameter list grows?
Run Code Online (Sandbox Code Playgroud)
代码示例的另一部分是这样的:
xor rax, rax
mov rbx, strng
mov rcx, strLen
mov r12, 0
pushLoop:
mov al, byte[rbx + r12]
push rax
inc r12
loop pushLoop
; It took me a few seconds to find out where the exit condition is. I realized that
; rcx is being compared to r12 in some way, but I'm not sure how. Is it explained anywhere?
Run Code Online (Sandbox Code Playgroud)
我不确定我应该在哪里寻找我的第一个问题的答案。我的预感是我的第二个问题的答案在 NASM 文档中的某个地方,但我不确定在哪里可以找到它。我试图将这些结构与我在高级语言中所知道的联系起来,但我很挣扎。
谢谢!
第一部分:所有库函数都遵循标准调用约定。在除 Windows 之外的所有 x86-64 平台上,这是 x86-64 System V ABI。
i386 和 x86-64 上的 UNIX 和 Linux 系统调用的调用约定是什么- 具体来说,整数/指针参数在 x86-64 System V 中按 RDI、RSI、RDX、RCX、R8、R9 的顺序传递。进一步的参数继续堆栈。(并且 RSP 在 a 之前总是必须是 16 字节对齐的call)。
您还可以查看进行函数调用或接受 args 的 C 函数的编译器输出,以查看是否正确,如果您知道要查找的内容。(例如,使用gcc -S -fverbose-asm,或在https://godbolt.org/ 上)。
在编写自己的 asm 函数时,您可以制定自己的约定,例如在多个寄存器中返回多个不同的值,而不是将自己限制为只能让 C 编译器执行的操作。
(例如,您可以编写一个memcmp返回 RDI 中第一个差异的位置和 FLAGS 中实际 < = 或 > 的位置,例如从不cmp匹配的字节上执行 a 。)
但是您可以从 asm 调用的编译器生成的函数(包括 C 标准库函数)将始终遵循 ABI。
第二部分:某些指令隐式使用寄存器:查看ISA手册以获取相关指令。如果您不知道,请不要仅从名称中进行假设。
您可以单步调试一个突出显示寄存器值更改的调试器,以帮助您注意到任何您根本没有预料到的寄存器更改的情况。
在 Intel 的 vol.2 手册(或 AMD 的等效手册)中查找说明。例如英特尔在https://www.felixcloutier.com/x86/ 上的 PDF 的 HTML 摘录 ,特别是loop. 另外x86 LOOP 指令究竟是如何工作的?解释说它就像一个dec rcx / jnz没有设置 FLAGS的except。
没有那么多带有隐式操作数的指令。最常用的是堆栈指令,如以明显的方式隐式使用 RSP 的 push/pop。
其他值得注意的包括 E/RAX 和 E/RDX 被单操作数[i]mul和 使用[i]div。(cdq并将 EAX 符号扩展到 EDX:EAX 以设置 idiv,或cdqe到 RAX)
用于可变移位计数的 CL 隐含在机器代码中,但在 asm 源代码中是显式的(如shr rdx, cl)。
rep-"string" 指令隐式使用 RCX,加上 RSI 和/或 RDI。
大多数这些隐含用途来自旧的 8086 历史。请参阅为什么没有包含 EAX 高字节的寄存器?. 编译器不使用likeloop和jrcxznot指令,因为它们很慢,并且当您不需要 EDX/RDX 中的高半结果时,imullike的 2 操作数形式imul ecx, edx更快。
进一步阅读:
lods和movs)这不是一个详尽的清单。cmpxchg / cmpxchg16b、xlat、cpuid、rdtsc、rdpmc 和许多其他指令都有隐式操作数,但只有少数编译器经常使用的指令可以。
请注意, FLAGS 是许多指令的隐式输入,例如adc和cmov。
NASM 有一个附录列出了所有指令,但通常汇编程序将其留给 CPU 供应商。所有 x86-64 汇编器都为相同的指令生成机器代码。该文档旧版本的这个错误修复分支保留了英文说明说明。(在添加 SSE 指令后,主线 NASM 删除了它以节省空间;如今,对于 AVX2,尤其是 AVX512,除了在一个平面页面中列出之外,要做的事情太多了。)
| 归档时间: |
|
| 查看次数: |
452 次 |
| 最近记录: |