我试图学习装配,这在某种程度上是有意义的,但我有一个问题。我有这个源文件hello.sfml
:
; nasm -felf64 hello.asml && ld hello.o
global _start
section .text
_start:
; write(1, message, 13)
mov rax, 1 ; syscall 1 is write
mov rdi, 1 ; file handle 1 is stdout
mov rsi, message ; address of string to output
mov rdx, 13 ; number of bytes in the string
syscall ; invoke OS to write the string
; exit(0)
mov rax, 60 ; syscall 60 is exit
xor rdi, rdi
syscall ; invoke OS to exit
message:
db "Hello, World", 10 ; the 10 is a newline character at the end
完美无缺。我只是不明白为什么在不同的情况下需要使用特定的整数寄存器。
例如,通过反复试验,我发现在说出我想要的系统调用时,例如。
mov rax, 1
...
syscall
我将值1
放入整数寄存器rax
,但我也可以使用整数寄存器eax
,ax
,al
或{ {1}}。
我还没有长时间学习装配,所以很可能是一个显而易见的问题。
如果我的问题不明显:我想知道如何决定将值移到哪个整数寄存器,例如如果有一些通用系统,或者每个不同意图使用不同的整数寄存器。
我在64位Ubuntu上使用NASM。
修改:我的问题与this one,不重复,因为在那里询问您将使用较小的整数寄存器的位置,我和#39; m要求一种方法来决定使用哪个整数寄存器。
答案 0 :(得分:3)
汇编或x86机器没有定义您应该使用的通用寄存器(GPR),您可以使用任何可用的GPR(或使其可用),但是,不同的环境定义了寄存器使用和参数的不同约定通过,当你想要使用他人时#39;代码你必须遵守这些约定。
具体来说,Linux x86-64正在使用以下约定,如X86 psABI(第3.2.3节)中所述:
- 如果该类为INTEGER,则使用序列%rdi,%rsi,%rdx,%rcx,%r8和%r9的下一个可用寄存器。
醇>
如果是标准的用户级代码,那就是在上面第一个示例中选择rdi
,rsi
和rdx
的原因,第一个参数是传入的rdi
,rsi
中的第二个,rdx
中的第三个。
但是,上面的例子演示了syscalls的Linux内核调用约定,它类似于用户级应用程序,但有一些区别(第A.2.1节):
- 用户级应用程序使用整数寄存器来传递序列%rdi,%rsi,%rdx,%rcx,%r8和%r9。内核接口使用%rdi,%rsi,%rdx,%r10,%r8和%r9。
- 系统调用通过syscall指令完成。内核会破坏寄存器%rcx和%r11。
- 系统调用的号码必须在寄存器%rax中传递。
醇>
正如您在示例中所看到的,每个系统调用都根据Linux System Call Table for x86-64定义了rax值(由zx485评论)。
请注意,系统调用可能有最多6个参数,与用户级代码不同,不能使用堆栈来获取其他参数。
Windows有不同的ABI,32位或其他环境,但我不会在这里详细说明。
关于您对al
,ax
和eax
的使用情况的评论:使用x86-64架构时,要求是使用任何其他部分指定rax中的系统调用次数寄存器是基于运气 - 如果寄存器其他部分的所有位都为零,那么你可以使用低位 - 但你不应该相信它。
提醒:
rax is the full 64-bit register
eax is the lower 32-bits
ax is the lower 16-bits
al is the lower 8 bits
ah is the value in bits 8 through 15
正如您所看到的,使用ah
是错误的,可能会调用另一个系统调用!