ADRP是一个PC-relative的取址指令,查找代码段中的一个指令的地址;
所谓的PC-relative是基于当前PC的值来计算,英文解释如下:
Form PC-relative address adds an immediate value to the PC value to form a PC-relative address, and writes the result to the destination register.
指令的编码格式如下:
计算方法:
bits(64) imm;
imm = SignExtend(immhi:immlo, 64);
bits(64) base = PC[];
X[d, 64] = base + imm;
其中immhi 【5, 23】与immlo【29,30】是直接从汇编指令中提取出来的。
哪这个的imm是从哪里来的呢?
imm是编译代码的时候根据label和pc值计算出来的。
例如:
pc: adrp x2, init_idmap_pg_dir
在编译代码的时候 ,计算出imm:
imm = (PAGE_ALIGN(&init_idmap_pg_dir) - PAGE_ALIGN(pc))/4KB
然后将imm[2, 21]保存到指令编码的[5, 23]位段中,将imm[0, 1]保存到指令编码的[29,30]位段中;
imm其实是init_idmap_pg_dir地址与pc之间的offset,单位是4KB;
在执行到“pc: adrp x2, init_idmap_pg_dir”时,就可以根据当前pc的值换算出init_idmap_pg_dir的地址:
PAGE_ALIGN(&init_idmap_pg_dir) = PAGE_ALIGN(pc) + imm * 4KB;
这里看出获取到的只是init_idmap_pg_dir以4KB对齐的基地址,如果要算出init_idmap_pg_dir的准确地址,还要用到ADD指令:
init_idmap_pg_dir = PAGE_ALIGN(&init_idmap_pg_dir) + init_idmap_pg_dir【0, 11】
由于imm占用21bit,其中最高bit为符号位,所以可以表示的offset范围是[-1MB, 1MB], 同时offset的单位是4KB,所以通过ADRP可以查找[pc - 4GB, pc + 4GB)之间的地址。
另外还有个ADR的指令,这个指令与ADRP类似,只是offset的单位是Byte,可以查找[pc - 1MB, pc + 4MB)之间的地址, ADRP中的P就是Page的意思。