SylixOS中AARCH64跳轉表實現原理

1. 跳轉表存在的意義

1.1 內核模塊反彙編

如下的程序清單,爲一個內核模塊的源碼。

#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <module.h>

/*
 *  SylixOS call module_init() and module_exit() automatically.
 */
int module_init (void)
{
    printk("hello_module init!\n");
    return 0;
}

void module_exit (void)
{
}

反彙編之後的內容如下所示。

kmTest.ko:     file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <module_init>:
/*
 *  SylixOS call module_init() and module_exit() automatically.
 */
int module_init (void)
{
   0:   a9bf7bfd    stp x29, x30, [sp, #-16]!
   4:   910003fd    mov x29, sp
    printk("hello_module init!\n");
   8:   90000000    adrp    x0, 0 <module_init>
   c:   91000000    add x0, x0, #0x0
  10:   f9400000    ldr x0, [x0]
  14:   94000000    bl  0 <API_LogPrintk>
    return 0;
  18:   52800000    mov w0, #0x0                    // #0
}
  1c:   a8c17bfd    ldp x29, x30, [sp], #16
  20:   d65f03c0    ret
  24:   d503201f    nop
    ...
0000000000000030 <module_exit>:
void module_exit (void)
{
}
  30:   d503201f    nop
  34:   d65f03c0    ret

從以上反彙編結果可知,printk函數調用會被彙編爲BL指令,並且跳轉的目的地址爲0,這是因爲實際的跳轉地址會在動態加載時進行調整。

94000000    bl  0 <API_LogPrintk>

1.2 BL指令分析

查閱ARMv8手冊,BL指令的結構如下圖所示。
BL指令結構
按照該結構可知,BL指令最大的跳轉範圍爲4×226 = 256MB,即±128MB。但是“實際需跳轉位置”與“當前指令位置”的地址偏移很有可能超過該範圍。所以在動態加載時,需要修改這條指令的實現,使得其具有跳轉到整個64位地址空間的能力。

2. AARCH64跳轉表實現

2.1 利用跳轉表進行跳轉

通常的做法是採用跳轉表進行實現。
跳轉表使用的方式如下圖所示,其中“跳轉表所在的位置”與“當前指令位置”的地址偏移範圍爲±128MB之內,因此,可以首先從當前位置跳轉到跳轉表中的某一個表項。
利用跳轉表進行跳轉

2.2 BR跳轉指令

BR跳轉指令使用寄存器進行跳轉,那麼該指令具體264地址空間跳轉的能力,因此跳轉表可以藉助該指令進行實現。

2.3 跳轉表結構

字節 指令內容
[16:19] movn x16, #0x….
[12:15] movk x16, #0x…., lsl #16
[08:11] movk x16, #0x…., lsl #32
[04:07] movk x16, #0x…., lsl #48
[00:03] br x16

因爲MOV指令不能一次將一個64位數移入寄存器,所以必須將移位操作分爲四步完成,如上表所示。
此時,在動態加載時,按照如下方式進行跳轉:
1、 將原來的BL指令中目的跳轉位置,調整爲跳轉表對應表項的位置;
2、 跳轉表會將實際跳轉地址更新到X16寄存器中;
3、 通過BR指令跳轉到實際的目標地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章