adrp 命令为何能获取符号的地址

3 篇文章 0 订阅

疑问所在

在linux 启动时,在如下位置会将bootloader 传入的x0 - x3 参数保存到boot_args[] 变量中。代码如下:

/*                                                                                                                                                                                                              
 * Preserve the arguments passed by the bootloader in x0 .. x3                      
 */                                                                                 
preserve_boot_args:                                                                 
    mov x21, x0             // x21=FDT                                              
                                                                                    
    adr_l   x0, boot_args           // record the contents of                       
    stp x21, x1, [x0]           // x0 .. x3 at kernel entry                         
    stp x2, x3, [x0, #16]                                                           
                                                                                    
    dmb sy              // needed before dc ivac with                               
                        // MMU off                                                  
                                                                                    
    mov x1, #0x20           // 4 x 8 bytes                                          
    b   __inval_dcache_area     // tail call                                        
ENDPROC(preserve_boot_args)

其中adr_l x0, boot_args 命令就是获取boot_args 的地址并存到x0 寄存器。
adr_l 的定义在arch/arm64/include/asm/assembler.h 中,如下:

    .macro  adr_l, dst, sym                                                         
    adrp    \dst, \sym                                                              
    add \dst, \dst, :lo12:\sym                                                                                                                                                                                  
    .endm 

将adr_l x0, boot_args 展开,如下:

adrp x0, boot_args
add x0, x0, :lo12:boot_args

即先获取boot_args 的页地址,再加上boot_args 的低12位,即得到boot_args 的地址。
看到这里我有如下两个疑问:

  1. 此时x0 保存的是物理地址还是虚拟地址?
  2. boot_args 定义在某个.c 中,此时mmu 未打开,页表也未建立,为什么adrp 指令能拿到boot_args 的地址?

adrp arm 官方文档

ARM 官方文档中对adrp 的解释如下
adrp 官方文档
即,adrp 的作用是:
= (当前的 PC 地址所在page 的基地址) + (imm)

其中,imm 为label 所在page 地址 与 当前pc 的page 地址的差。
也就是说,label 在编译时,已经被替换成了一个偏移量。

adrp 实例

将vmlinux 反汇编如下:

ffff800011310020 <preserve_boot_args>:
ffff800011310020:       aa0003f5        mov     x21, x0
ffff800011310024:       d00042a0        adrp    x0, ffff800011b66000 <boot_args>
ffff800011310028:       91000000        add     x0, x0, #0x0
ffff80001131002c:       a9000415        stp     x21, x1, [x0]
ffff800011310030:       a9010c02        stp     x2, x3, [x0,#16]
ffff800011310034:       d5033fbf        dmb     sy
ffff800011310038:       d2800401        mov     x1, #0x20                       // #32
ffff80001131003c:       17b65539        b       ffff8000100a5520 <__inval_dcache_area>
...
ffff800011b66000 <boot_args>

d00042a0 这个指令,参考图“adrp 官方文档”,各个字段的值如下:
immhi = 0x215,immlo = 0x2
imm = ( (immhi << 2) | immlo ) << 12 = 0x856000
Xd = x0
adrp 指令所在的pc 为 0xffff800011310024

则指令 d00042a0 等效为:
x0 = pc & 0xfffffffffffff000 + imm

x0 = (0xffff800011310024 & 0xfffffffffffff000) + imm = 0xffff800011b66000
即 boot_args 的地址。

其实,preserve_boot_args 这个阶段,mmu 还没有打开,pc 并非链接地址 0xffff800011310024,而是一个物理地址。不过该指令与boot_args 地址的offset 是固定不变的,即imm,所以最终仍能得到正确的boot_args 的物理地址。

总结

adrp 在编译时,会记录label 地址与当前指令地址的offset。在运行时,pc + offset 就能得到label 的地址。
又因为arm64 指令长度的限制,所以adrp 这条指令得到的地址仅仅精确到了label 所在的页的基地址,需要配合ADD 加上label 的lo12 位才能得到准确地址。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值