RIP相关寻址的正确用法及x64汇编代码适配疑问
x86到x64汇编适配的技术问题解答
我原本有一段x86架构下查找data_items数组最大值的汇编代码,因为x64系统不支持32位绝对寻址,所以将其适配为x64架构的代码,核心操作如下:
lea data_items(%rip), %rdi #(1) 获取data_items的地址 add $4, %rdi #(2) 将指针偏移4字节,读取下一个元素 movl (%rdi), %eax #(3) 将%rdi指向的内存数据读取到%eax
针对这段适配代码,我有两个技术问题,以下是对应的解答:
问题1:这种指针指向方式是否正确?代码重定位后是否会产生错误?
这种指针指向方式是完全正确的,重定位后也不会出现错误。x64架构中,RIP相对寻址是访问静态数据的标准推荐方式,lea data_items(%rip), %rdi正是这种用法的典型实现。链接器在处理程序时,会自动计算data_items与当前lea指令下一条指令地址的固定偏移量,无论程序最终加载到内存的哪个基地址,这个偏移量都不会改变,因此重定位后依然能准确获取数组的起始地址。
问题2:既然%rip寄存器的值持续增长,为何lea data_items(%rip), %rdi能加载正确的内存地址?RIP偏移是否有特殊含义而非简单的“dataItems + %rip”?
首先要明确:RIP寄存器在指令执行时的值,是当前正在执行的这条指令的下一条指令的地址。lea data_items(%rip), %rdi中的偏移量,是链接阶段就计算好的data_items地址与该lea指令下一条指令地址的差值。实际计算逻辑是data_items = 当前RIP值 + 固定偏移量,反过来就是当前RIP值 + 固定偏移量 = data_items。
这条lea指令执行时,RIP的值是固定的(就是它下一条指令的地址),偏移量也是链接时确定的固定值,所以两者相加必然得到data_items的正确绝对地址,和后续RIP的增长没有关系——因为这条指令只用到了它执行瞬间的RIP值。
完整适配后的x64汇编代码
.section __DATA,__data data_items: .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0 .section __TEXT,__text globl _main _main: lea data_items(%rip), %rdi #(1) 获取数组起始地址 movl (%rdi), %eax movl %eax, %ebx start_loop: cmpl $0, %eax je loop_exit add $4, %rdi #(2) 指针偏移4字节(long类型占4字节) movl (%rdi), %eax #(3) 读取下一个元素 cmpl %ebx, %eax jle start_loop movl %eax, %ebx jmp start_loop loop_exit: mov $0x2000001, %rax mov $0, %rdi syscall
内容的提问来源于stack exchange,提问作者Arsynth
相关产品推荐
相关产品推荐

