本文基于ARM64平台代码分析,ARM64平台的内核,在编译链接时,kernel代码段被链接的位置是:KIMAGE_VADDR + TEXT_OFFSET。我们可以通过查看vmlinux.lds.S链接文件查看具体内容:
. = KIMAGE_VADDR + TEXT_OFFSET;
.head.text : {
_text = .;
HEAD_TEXT
}
下面依次介绍各个变量。
默认情况下:
TEXT_OFFSET := 0x00080000
内核中定义KIMAGE_VADDR实际上就是vmalloc区域的起始地址,也就是MODULES_END处的地址。
#define KIMAGE_VADDR (MODULES_END)
从这一段,我们可以得到一个重要的关系,那就是KIMAGE_VADDR的值等于内核编译链接地址减去TEXT_OFFSET。
ARM64平台运行时,kernel代码段被映射到虚拟地址空间,如果没有使能kaslr特性,那么被映射的地址就是链接的地址处,如果使能了kalsr,那么实际映射的地址可能就与链接的地址不一样,中间会多出来一个偏差kaslr offset,先暂且不管这这种情况,先看如下定义:
ENTRY(kimage_vaddr)
.quad _text - TEXT_OFFSET
从这个计算关系中,内核在运行时定义了kimage_vaddr,它的值等于内核实际运行地址_text减去TEXT_OFFSET。
kaslr特性会对内核加载地址做relocation,上面也做了一点介绍,使能该特性后,内核实际映射的运行时地址和链接地址是不一样的,中间差距kaslr offset值。
那么它是怎么实现的呢?
在内核代码被映射到虚拟地址空间时,参考 __create_page_tables:
函数,默认是加载到链接地址处,也就是KIMAGE_VADDR + TEXT_OFFSET
位置处,但是使能了kaslr特性后,就会在映射前对地址做一个偏移,也就是kaslr offset值。
mov_q x5, KIMAGE_VADDR + TEXT_OFFSET // compile time __va(_text)
add x5, x5, x23 // add KASLR displacement
由此我们得知:
kernel_run_address = KIMAGE_VADDR + TEXT_OFFSET + kaslr offset
而通过前面对kimage_vaddr的定义又可以知道:
kernel_run_address = kimage_vaddr + TEXT_OFFSET
因此可得:
kimage_vaddr + TEXT_OFFSET = KIMAGE_VADDR + TEXT_OFFSET + kaslr offset
最终推导:
kaslr offset = kimage_vaddr - KIMAGE_VADDR
内核中也是函数用于计算kaslr_offset值的:
static inline unsigned long kaslr_offset(void)
{
return kimage_vaddr - KIMAGE_VADDR;
}
当我们在进行debug时,必须先把内核中运行的地址转换为链接地址,然后才可以使用addr2line工具进行解析,因此第一步需要计算出kaslr offset:
kernel_run_address = kernel_link_address + kaslr_offset
利用上述关系,可以推导出kaslr_offset,后续在根据内核中对应的运行时的地址,反推出链接地址即可利用addr2line解析链接地址处的symbol。
symbol_link_address = symbol_run_address - kaslr_offset
参考:
http://www.wowotech.net/memory_management/441.html
http://www.wowotech.net/memory_management/436.html