汇编程序指针地址 - 它是如何获得的

assembler pointer address - how does it gets it

我对汇编程序级别的指针有很大的误解。我知道要获取指针(变量)的地址,您可以使用

MOV EAX, variable
or 
LEA EAX, variable

而且我也知道 MOV 有许多不同的 OP 代码,但我感兴趣的是 MOV register, address

问题是……它如何知道变量位于哪个地址?汇编程序是否已经在可执行文件中生成 MOV EAX, address_of_variable 或者指令是 MOV EAX, ... 并且它找到该值然后获取它的地址。但是它怎么知道如何搜索内存以获取该值,然后 return 地址(假设地址每次都改变)。

如果这是一个愚蠢的问题,请原谅我,但我不明白它是如何做到的。

在标准体系结构中,您可以在不同的地方存储变量:

  • 数据Segment/BSS:对于全局变量和静态局部变量(具有静态存储期限的变量);
  • 堆栈:局部变量(自动存储时长的变量);
  • Heap:动态变量(具有动态存储持续时间的变量,使用newmalloc类函数分配)。

编译器如何解析变量的地址取决于变量的存储位置。

数据Segment/BSS

这些是具有静态存储持续时间的变量,编译器在编译时知道关于它们的所有信息,并且它们的地址在 link 时已知。采取以下片段:

int a = 0;

int main () {
    return a;   
}

程序集(减少):

a:
        .zero   4
main:
        ...
        movl    a(%rip), %eax
        ...

你有a所在的数据段(程序集的顶部),要获取a的地址,编译器使用a(%rip)(指定x86程序集) .这将由 linker 在 link 时 "replaced" - 没有 "dynamic" 地址计算。

注意:没有"dynamic"计算意味着在运行时没有指令用于计算地址,但是这些地址"live" 在 virtual address space (relative to the program itself), and will be translated to physical address at run-time by the memory management unit.

这可能是最直接的存储类型:变量的地址已经存储在某个地方,因此您只需检索它即可。采取以下片段:

int main () {
    int *p = new int();
    return *p;      
}

程序集:

main:
        ...
        movq    -8(%rbp), %rax # Load p into rax (see the stack section)
        movl    (%rax), %eax   # Load *p into eax
        ...

第一条汇编指令的解释见堆栈部分,但基本上是将p的值存入rax寄存器,然后用(%rax)取回rax 中地址存储的值(如果 rax 包含 0x8000,则 (%rax) 是地址 0x8000 中的值)。

堆栈

自动存储时间的变量存储在栈中,其地址是相对于栈指针(它是一个始终指向栈"base"的寄存器)获取的。采取以下片段:

int f() {
    int a = 4;
    return a + 2;
}

程序集(减少):

f():
        ...
        movl    , -4(%rbp)     # Set a = 4
        movl    -4(%rbp), %eax   # Load a into eax
        addl    , %eax         # Add 2 to eax
        ...

在上面的程序集中,rbp基指针,正如你所看到的,a的地址是相对于它检索的-4(%rbp)(第一条指令基本就是a = 4)。

注意: return 值存储在 eax 寄存器中,如果有,函数的参数也会放在堆栈,但此行为取决于 calling convention,其他架构可能使用不同的方法。