c - 从机器的角度理解汇编程序中的指针
问题描述
这是我在 Godbolt 编译器上编写的一个基本程序,它很简单:
#include<stdio.h>
void main()
{
int a = 10;
int *p = &a;
printf("%d", *p);
}
我得到编译后的结果:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-12], 10
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
leave
ret
问题:推rbp,通过制作一个16字节的块来制作堆栈帧,如何从寄存器中将值移动到堆栈位置,反之亦然,LEA的工作是如何找出地址,我得到了这部分.
问题:
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
Lea -> 将 rbp-12 的地址放入 rax,然后将 rbp-12 的地址的值移动到 rax,但下一行又说,移动到 rax,即 rbp-8 的值。这似乎模棱两可。然后再次将 rax 的值移动到 eax。我不明白这里的工作量。为什么我做不到
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov eax, QWORD PTR [rbp-8]
并完成它?因为在原始行中,rbp-12 的地址存储到 rax,然后 rax 存储到 rbp-8。然后rbp-8再次存储到rax中,然后rax再次存储到eax中?我们不能直接将 rbp-8 复制到 eax 吗?我猜不是。但我的问题是为什么?
我知道指针中有取消引用,所以 LEA 如何帮助获取 rbp-12 的地址,我理解,但在接下来的部分中,它是什么时候从我完全丢失的地址中获取值的。而且,在那之后,我不明白任何 asm 行。
解决方案
您会看到非常未优化的代码。这是我的逐行解释:
.LC0:
.string "%d" ; Format string for printf
main:
push rbp ; Save original base pointer
mov rbp, rsp ; Set base pointer to beginning of stack frame
sub rsp, 16 ; Allocate space for stack frame
mov DWORD PTR [rbp-12], 10 ; Initialize variable 'a'
lea rax, [rbp-12] ; Load effective address of 'a'
mov QWORD PTR [rbp-8], rax ; Store address of 'a' in 'p'
mov rax, QWORD PTR [rbp-8] ; Load 'p' into rax (even though it's already there - heh!)
mov eax, DWORD PTR [rax] ; Load 32-bit value of '*p' into eax
mov esi, eax ; Load value to print into esi
mov edi, OFFSET FLAT:.LC0 ; Load format string address into edi
mov eax, 0 ; Zero out eax (not sure why -- likely printf call protocol)
call printf ; Make the printf call
nop ; No-op (not sure why)
leave ; Remove the stack frame
ret ; Return
编译器在不进行优化时,会在解析您提供给他们的代码时生成这样的代码。它做了很多不必要的事情,但生成速度更快,并且使用调试器更容易。
将此与优化后的代码 (-O2) 进行比较:
.LC0:
.string "%d" ; Format string for printf
main:
mov esi, 10 ; Don't need those variables -- just a 10 to pass to printf!
mov edi, OFFSET FLAT:.LC0 ; Load format string address into edi
xor eax, eax ; It's a few cycles faster to xor a register with itself than to load an immediate 0
jmp printf ; Just jmp to printf -- it will handle the return
优化器发现变量不是必需的,因此没有创建堆栈帧。除了电话,什么都没有了printf
!这是作为 a 完成的,jmp
因为完成后这里不需要做任何其他事情printf
。