【问题标题】:assembler pointer address - how does it gets it汇编器指针地址 - 它是如何得到它的
【发布时间】:2016-08-23 07:29:34
【问题描述】:

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

MOV EAX, variable
or 
LEA EAX, variable

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

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

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

【问题讨论】:

  • MOV EAX, variable 在 NASM 语法中组装成 mov eax, imm32 操作码。汇编器留下 4 个字节的零作为数据,链接器用该符号引用的绝对地址填充它(仅在链接时才知道)。从来没有任何“搜索记忆”。在 MASM 语法中,mov eax, variable 是来自该变量的加载(但同样,绝对地址由链接器填充,作为寻址模式的 disp32 而不是 imm32)。在 NASM 语法中,lea eax, variable 是语法错误;也许你在想lea eax, [variable]
  • 所以当我运行程序时链接器运行?当我执行程序时,链接器在我的代码之前运行?
  • 链接器是从汇编器输出创建可执行文件的东西。 .asm -> .o 是汇编程序。 .o -> a.out(或.exe)是链接器。
  • 链接时怎么知道?链接器是否将显式内存地址放入可执行文件?如果是 .so 或 .dll ,地址每次都会改变?我知道它使用虚拟地址,并且每个程序都相同,但我仍然不明白.. :(
  • 它在链接时是已知的,因为可执行文件不是位置独立的。 (它们被加载到一个固定的地址,mov eax, imm32 地址利用了这一点)。您不能在共享库或其他需要 PIC(位置独立代码)的地方使用 mov eax, var。我将写下这些 cmets 作为答案。我应该使用什么操作系统作为示例?哪个汇编器:NASM 风格还是 MASM 风格? 32 位还是 64 位(RIP 相关的 LEA 解决了 PIC 问题)?

标签: pointers assembly x86 memory-address


【解决方案1】:

在标准架构上,您可以在多个地方存储变量:

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

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

数据段/BSS

这些是具有静态存储持续时间的变量,编译器在编译时了解它们的所有信息,并且它们的地址在链接时是已知的。取下面的sn-p:

int a = 0;

int main () {
    return a;   
}

组装(简化):

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

您有a 所在的数据段(程序集顶部),并且要获得a 的地址,编译器使用a(%rip)(指定x86 程序集)。这将在链接时由链接器“替换” - 没有地址的“动态”计算。

注意:没有“动态”计算意味着在运行时没有用于计算地址的指令,但是这些地址“活”在virtual address space 中(相对于程序本身),并在运行时由memory management unit 转换为物理地址。

这可能是最直接的存储类型:变量的地址已经存储在某个地方,因此您只需检索它。取下面的sn-p:

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)检索存储在@中地址的值987654336@(如果rax包含0x8000,则(%rax)是地址0x8000处的值)。

堆栈

具有自动存储持续时间的变量存储在堆栈上,其地址是相对于堆栈指针(始终指向堆栈的“基”的寄存器)检索的。取下面的sn-p:

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

组装(简化):

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

在上面的汇编中,rbp基指针,你可以看到,a 的地址是由-4(%rbp) 相对于它检索的(第一条指令基本上是a = 4)。

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

【讨论】:

  • 在您的堆示例中,命名变量 int *p 是具有自动存储(即堆栈)的本地变量。我想这确实说明了动态存储与静态存储的不同之处,因为该位置仅在运行时才知道,并且可以是一个值,但不能是变量的地址。
  • movl -4(%rbp), %eax # Store a into eax 中的评论措辞不当。这是负载,不是商店,所以你应该说load a into eax
  • @PeterCordes 我用Load 更新了答案。关于堆部分,我主要说*p(因为,是的,p有自动存储时长),我加了一个小注释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多