汇编程序指针地址 - 它是如何获得的
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:动态变量(具有动态存储持续时间的变量,使用
new
或malloc
类函数分配)。
编译器如何解析变量的地址取决于变量的存储位置。
数据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,其他架构可能使用不同的方法。
我对汇编程序级别的指针有很大的误解。我知道要获取指针(变量)的地址,您可以使用
MOV EAX, variable
or
LEA EAX, variable
而且我也知道 MOV 有许多不同的 OP 代码,但我感兴趣的是 MOV register, address
。
问题是……它如何知道变量位于哪个地址?汇编程序是否已经在可执行文件中生成 MOV EAX, address_of_variable
或者指令是 MOV EAX, ...
并且它找到该值然后获取它的地址。但是它怎么知道如何搜索内存以获取该值,然后 return 地址(假设地址每次都改变)。
如果这是一个愚蠢的问题,请原谅我,但我不明白它是如何做到的。
在标准体系结构中,您可以在不同的地方存储变量:
- 数据Segment/BSS:对于全局变量和静态局部变量(具有静态存储期限的变量);
- 堆栈:局部变量(自动存储时长的变量);
- Heap:动态变量(具有动态存储持续时间的变量,使用
new
或malloc
类函数分配)。
编译器如何解析变量的地址取决于变量的存储位置。
数据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,其他架构可能使用不同的方法。