AFAIK X86-64增加了大量的通用寄存器的那些从英特尔86(派生rax,rcx等),被称为r8- r15.
他们为什么要这样命名新的寄存器?为什么不遵循现有的命名规则,并呼吁他们一样rfx,rgx...?
我开始在 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 …Run Code Online (Sandbox Code Playgroud) 读完这个问题后为什么 %cl 是 sal 操作接受作为参数的唯一寄存器?,我很好奇还有哪些指令需要计数寄存器?
\n循环控制
\nLOOP 指令假定 CX、ECX 或 RCX 寄存器包含循环计数。
\nloop\nloopnz\nloopne\nloopz\nloope\nRun Code Online (Sandbox Code Playgroud)\n跳跃
\n如果 CX、ECX 或 RCX 寄存器为零,则执行跳转的跳转指令。
\njcxz\njecxz\njrcxz\nRun Code Online (Sandbox Code Playgroud)\n重复前缀
\n这些重复前缀与字符串指令一起使用并使用计数寄存器。
\nrep\nrepe\nrepz\nrepne\nrepnz\nRun Code Online (Sandbox Code Playgroud)\n位移位
\n逻辑、移位、循环和位指令(省略了双字和四字变体,例如 shld、shlq...)
\nshl\nshr\nsal\nsar\nrol\nror\nrcl\nrcr\nRun Code Online (Sandbox Code Playgroud)\nFastcall 调用约定
\n参数列表中从左到右找到的前两个 DWORD 或更小的参数被传入ECXEDX 寄存器;所有其他参数都在堆栈上从右向左传递。
__attribute__((fastcall)) void printnums(int num1, int num2, int num3){\n printf("The numbers you sent are: %d %d %d", num1, num2, num3);\n}\n\nint main(){\n printnums(1, 2, 3);\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\nMicrosoft x64 …
info frame在我的机器上发出命令时(断点打开main),输出如下:
(gdb) info frame
Stack level 0, frame at 0x7fffffffdbd0:
rip = 0x4005b1 in main; saved rip = 0x7ffff7a53b05
Arglist at 0x7fffffffdbc0, args:
Locals at 0x7fffffffdbc0, Previous frame's sp is 0x7fffffffdbd0
Saved registers:
rbp at 0x7fffffffdbc0, rip at 0x7fffffffdbc8
Run Code Online (Sandbox Code Playgroud)
当我从了解这个答案,eip和ebp寄存器(在我的输出不存在),具有以下含义:
eip 是执行下一条指令的寄存器(也称为程序计数器)
“ebp”是通常被认为是这个栈帧的locals的起始地址的寄存器,它使用“offset”来寻址
从另一个答案中,我明白
【RIP是】指令指针
[...]
其中一些寄存器被设想用于特定用途,并且通常如此。最关键的是 RSP 和 RBP。
最后,info registers给我以下输出:
(gdb) info registers
rax 0x4005ad 4195757
rbx 0x0 0
rcx 0x0 0
rdx …Run Code Online (Sandbox Code Playgroud) 具体是:
mov %eax, %ds
Run Code Online (Sandbox Code Playgroud)
慢于
mov %eax, %ebx
Run Code Online (Sandbox Code Playgroud)
或者他们是相同的速度.我在网上研究过,但一直无法找到明确的答案.
我不确定这是否是一个愚蠢的问题,但我认为修改分段寄存器可以使处理器做额外的工作.
NB我关注旧的x86 linux cpus,而不是现代的x86_64 cpus,其中分段的工作方式不同.
我理解在函数的开头和结尾使用push rbp...pop rbp来保留rbp调用函数的值,因为rbp寄存器是被调用者保留的。然后我理解使用rbp作为当前正在执行的过程的堆栈帧的当前顶部的“约定” 。但与此相关,我有两个问题:
rbp只是一个约定?我可以像r11堆栈帧的基础一样轻松地使用(或任何其他寄存器甚至堆栈上的 8 个字节)吗?rbp寄存器有什么特别之处,或者它只是用作基于历史和约定的堆栈框架?mov %rbp, %rsp在离开函数之前用作“清理”方法?例如,push/pop指令通常是对称的,所以mov %rbp, %rsp只是一种速记方式,有人可以“跳过”执行对称的弹出/添加等操作?什么mov %rbp, %rsp是有用的实际用途?几乎所有时候我在编译器输出中看到它(启用零优化),它似乎是不必要的或多余的,而且我很难想到它实际上可能有用的场景。有些教程说EFLAGS寄存器是通用寄存器,而其他教程说它不是通用寄存器。
那么是哪一个呢?!
我对x86-64二进制编码很新.我正在尝试修复一些旧的"汇编程序"代码.
无论如何,我正在尝试做这样的事情(英特尔语法):
mov [rbp+rcx], al
Run Code Online (Sandbox Code Playgroud)
汇编程序目前正在生成:
88 04 0D
Run Code Online (Sandbox Code Playgroud)
但这似乎不是一个有效的指示.如果我将SIB字节中的基数更改rbp为其他寄存器,则可以正常工作.另一种使其工作的方法是添加一个零字节的位移(88 44 0D 00).这似乎与其他类似的操作码一起发生.
为什么我不能rbp在那里使用mod=00?
我并不真正理解 EBX 寄存器的用途。我通过搜索 EBX 的目的得到了不同的答案,但最普遍的共识是 EBX 没有像 EAX、ECX 或 EDX 那样的特殊目的。为什么我需要使用 EBX?我已经从简单的教程中删除了 EBX,该程序仍然有效。那么使用EBX的原因是什么?如果没有它我也能工作,我为什么要关心它?
assembly ×8
x86 ×6
x86-64 ×5
nasm ×2
c ×1
cpu-cycles ×1
gdb ×1
intel ×1
isa ×1
machine-code ×1
masm ×1
mov ×1
pointers ×1
stack-frame ×1