目录
背景
一、相关基础知识
1,MMU的作用
2,逻辑地址,虚拟地址,线性地址
二、页表转换寄存器描述符
1,页表/页目录结构
2,转换相关寄存器描述符
1.转换基址寄存器
2.转换描述符格式
3.用户/内核PGD表基地址
三、转换流程
四、实例分析
1,基于carsh_arm64(fulldump)测试代码流程
2,内核虚拟地址的转换举例
3,内核PMD映射举例
4,用户虚拟地址的转换举例
五,参考文档
六,附录:
附录1
附录2
附录3
附录4
背景
?????????最近死机dump问题的分析让人焦头烂额,也同时深深的感觉到内核功底的重要性,总结起来大部分死机问题的分析最终都指向了同一个地方,就是让人掉头发的linux内存管理,如果说异常模型是稳定性模块的必修课,那么内存管理就是第二门必修课了。
????????记得之前再阅读linux内核启动过程代码的时候,也想总结一篇armv8的启动流程(基于本期之后会出一篇详细的),但总是因为遇到paging_init等页表相关内存页函数和宏,最终半途而废,linux内存管理被公认为操作系统五大模块里面最难的一部分,果然没让人失望。
????????躲不过去的东西最终还是必须要面对的,这是变得强大的必经之路,因此决定对这部分内容做一个深入的分析理解,为后面的掰头内存管理打下良好的基础!!!
? ? ? ? 分析基于四个部分(重心放在第四部分), 第一个部分mmu相关内容,第二部分讲解虚拟地址转换物理地址相关寄存器描述符,第三部分转换流程,第四部分以两个例子深入详细讲解内核和用户虚拟地址如何转换为物理地址,另外还会结合内核相关的宏和函数,最终的目的当然是,达到理解虚拟物理地址,页表,的存在和意义,相信看完这篇文章(原创原创原创),对于linux arm64的页表一定会有系统的理解!!!
本文所有的分析都基于以下配置 !!!!!主机? ubuntu16.04,kernel4.14,crash_arm64_7.2
目标手机基于紫光展锐T612处理器,2大核Cortex-A75,6小核 Cortex-A55,aarch64,armv8,linux4.14,内存2G,4kpage,3级页表,39位虚拟地址
"arch/arm64/config/sprd_xxxx_defconfig"
CONFIG_ARM64_4K_PAGES=y // OFFSET大小为低12位,2^12 = 4k
CONFIG_ARM64_VA_BITS_39=y // 39位虚拟地址,2^39=512G kernel与usr的虚拟地址大小均为512G
CONFIG_PGTABLE_LEVELS=3 // 3级页表 PGD PMD PTE OFFSET
AArch64 Linux memory layout with 4KB pages + 3 levels:
Start End Size Use
-----------------------------------------------------------------------
0000000000000000 0000007fffffffff 512GB user
ffffff8000000000 ffffffffffffffff 512GB kernel
一、相关基础知识
1,MMU的作用
? ? ? ? 内存管理单元(Memory Management Unit)的一个重要功能是使系统能够运行多个任务,就像独立的程序在它们自己的私有虚拟内存空间中运行一样。他们不需要了解系统的物理内存映射(即硬件实际使用的地址),也不需要了解可能同时执行的其他程序。
? ? ? ? 每个程序都使用相同的虚拟内存地址空间。还可以使用连续的虚拟内存映射,即使物理内存是不连续的。这个虚拟地址空间与系统中内存的实际物理映射是独立的。不同的设备和处理器可能拥有不同的虚拟地址,和物理地址映射,MMU会根据具体的映射关系去处理具体情况下的转换。
????????TLB功能基于访存的局部性原理,他会缓存查询过的一段页表块,当下次需要查询相应块的页表时,可以快速从TLB中获取,大大提高页表的转换速度。
????????虚拟地址是指在内存中放置代码时,编译器和链接器使用的地址。物理地址是指实际硬件系统使用的地址。
????????MMU使用虚拟地址的指定的位数来索引转换表中的条目,并确定正在访问哪个块。MMU将代码和数据的虚拟地址转换为实际系统中的物理地址。转换由硬件自动执行,并且对程序是透明的。除了地址转换之外,MMU还控制每个内存区域的内存访问权限、内存顺序和缓存策略。
? ? ? ? Figure 1-1 是Arm核心访问内存的逻辑框图,在Linux内核在初始化完CPU创建完临时页表后就会使能MMU,而MMU一旦开启,处理器访问的都是虚拟地址,虚拟地址访问都需要经过MMU的转换才能指向真正的物理内存地址,
????????虚拟地址转换为物理地址的过程,需要依次查询页目录项和页表,其中TTBR1用来存储内核的全局页表基地址,TTBR0存储用户页表基地址,内核虚拟地址空间最终通过查询TTBR1_EL1页表最终找到对应的物理地址,用户地址空间最终通过查询TTBR0_EL0页表最终找到对应的物理地址;
2,逻辑地址,虚拟地址,线性地址
????????这里还要说明一下、逻辑地址,线性地址,虚拟地址,物理地址的关系:
逻辑地址:可以理解为程序经过编译链接之后(当我们把程序编译链接出来,逻辑地址就确定了),代码段被赋值的地址,(可以使用objdump -d? elffile)查看代码段地址;
线性地址:elf文件被加载到内存中后的地址,也就是分段单元为程序分配的线性地址,也是cpu(取指令),寄存器,所使用的地址,线性地址==虚拟地址;
物理地址:memory的实际地址,处理器通过使用这个地址在总线上和memory通信,达到读写的目的;
地址转换关系如图figure 2-1,
????????????????
由于linux arm64架构下面是没有分段的(或者说所有的进程段寄存器都是相同的值),分段只有x86架构下才有,分段的作用是把逻辑地址转换为虚拟地址,其实质就是为每个进程(数据代码段)分配不同的线性地址空间,而分页是把同一线性地址空间映射到不同的物理空间,而由第一节可以知道,linux? arm64下所有的程序的线性(虚拟)地址空间都是相同的为2^39=512G(范围ffffff8000000000-ffffffffffffffff);
?因此 linux arm64下,
逻辑地址==线性地址==虚拟地址。
二、页表转换寄存器描述符
1,页表/页目录结构
? ? ? ? ?基于前言中的内核配置,内核采用39位虚拟地址,因此可寻址范围为2^39 = 512G,采用(linux 默认为五级页表,另外还有PUD,P4D,由于本文只配置三级,其他两项不予罗列)3级页表结构,分别为:
PGD? ?(Page Global Directory)? ? ? ? ? ? ? ? ? ? bit[39:30]? ? ? level1
PMD ? (Page Middle Directory) ? ? ? ? ? ? ? ? ? ?bit[30:21]? ? ?level2
PTE? ? (Page Table)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bit[21:12]? ? ? level3
每一级索引占9bit,也就是每一个页目录表/页表都有2^9=512个页目录项/页表项,使用4k页面大小,2^12 = 4096,因此虚拟地址中offset字段占12位,每一个pte页表项可以映射4k个地址空间,共有512x512x512个pte页表项,总的可寻址地址空间为 512x512x512x4096 = 2^39 = 512Gb。
每一个页表项占用8个字节,每一张页表有512项,所以一张页表占用空间为512*8=4K,因此页表的基地址都是4k对齐的,也就是页表基地址的低12位都为0; 内核页表相关重要宏控,相信你仔细看过第三节的实例之后,对这些宏控都会有清晰得理解!!
pgd_offset(mm,addr) ---接受内存描述符mm,和一个虚拟地址作为参数,这个宏产生addr在页全局目录在相应表项中的线性地址
pgd_offset_k(addr) ---用来产生内核页全局目录在相应表项中的线性地址
pgd_index(addr) ---从addr中提取页全局目录表项的索引
pmd_index(addr) ---从addr中提取页中间目录表项的索引
pmd_offset(pud,addr) ---接受页上级目录指针,和虚拟地址作为参数,这个宏产生目录项addr在页中间目录项中的偏移地址
pte_index(addr) ---从addr中提取页中间目录表项的索引
pge_offset_kernel(dir,addr) ---线性地址addr在页中间目录dir中有一个对应项,该宏就产生这个对应项,即页表的线性地址
2,转换相关寄存器描述符
1.转换基址寄存器
TTBR1?:(Translation Table Base Register)转换表基地址寄存器,用来存放内核(init_mm.pgd)PGD全局转换表的基地址;
TTBR0 :(Translation Table Base Register)转换表基地址寄存器,用来存放用户(task_struct.mm.pgd)PGD全局转换表的基地址;
2.转换描述符格式
? ? ? ? armv8规定,所有页级别都是用同一个描述符格式,PGD只能输出下一级目录表的基地址。PTE描述符不能指向另一个表的基地址,只能输出块地址。那么反过来PMD,PUD是可以直接输出地址块的,也就是直接指向一个块地址,下一级页表就不会再被映射,这种情况我们称之为巨页,
pte 映射? ?页大小=4k? ? 2^12
pmd 映射 页大小=2m? ? 2^(12+9)
pud映射? 页大小=1G? ? ?2^(12+9+9)
可以在附录2 表的第三列看到这种情况,这里我们的描述以实例只针对pte的映射,也就是页大小4k。
所有页目录/页表项描述符由低bit[1:0]位指出,有以下三种情况:
a,下一级表的地址,在这种情况下,内存可以进一步细分为更小的块(页表级数越多,pte映射的页大小就越小)。
b,可变大小的内存块的地址(如果为块地址,其还需要加上va[offset]才能构成物理地址,所以称可变大小)。
c,可以标记为Fault或Invalid(无效条目)。
我们主要关注bit[1:0],由图可以得出以下重要结论,页表项中:
当bit[1:0]={0,1}时,为block entry ,其中间部分为物理地址的高位PA[39:12]?;
当bit[1:0]={1,1}时, 为table entry,其中间部分[39:12]为下一级页表的物理基地址;
当bit[1:0] = {1,0|0,0}时,该表项为无效项;
????????转换表描述符中lower attributes中存储相关属性信息,mmu在查找到相应的表项时,首先会查询属性信息,确认地址的相关属性(可执行权限,访问权限,共享属性,访问标志,安全标志等)后,根据需要取出下一级页表的基地址。
3.用户/内核PGD表基地址
????????基于之前的分析可知,用户虚拟地址和内核虚拟地址转换为物理地址的时候使用不同的页表基地址寄存器(TTBRx),因此他们的转换是基于不同的全局页目录表PGD。
????????其中内核全局页目录表PGD存储在init_mm.pgd中,我们知道内核是常驻内存的,因此内核的PGD表只有一份,他不会因为进程的切换而改变,所有内核地址访问都依赖这一个PGD表;用户全局页目录表PGD存储在进程描述符task_struct.mm.pgd中,他是在用户进程被创建时同步被创建的,每一个进程描述符task_struct都对应有自己的task_struct.mm.pgd表,进程所有地址的访问都依赖于对应的task_struct.mm.pgd页表的查询,因此在进程切换时,TTBR0中的值(task_struct.mm.pgd)是要同时改变的,这也与linux中每一个进程都独占整个虚拟(此为512G)地址空间相对应;
三、转换流程
据此可以画出如下转换框图:
MMU在转换虚拟地址的时候遵循以下步骤(基于以上配置):
1,如果虚拟地址bit[63:40]都为1,则使用TTBR1作为第一级页目录表基地址,当bit[63:40]都为0时,使用TTBR0作为第一级页目录表基地址;
2,PGD包含512个64位PMD表,从虚拟地址中获取VA[39:31]进行索引,找到对应条目为PGD+index[39:31]);
4,MMU检查PGD目录项的有效性(bit[1:0]),以及其属性标志判断是否允许请求的内存访问。假设它有效,且允许访问内存;
5,MMU从PGD目录表项中获取bit[39:12],作为PMD页表的物理基址(table descriptor)。
6,PMD包含512个64位PTE表,从虚拟地址中获取VA[30:21]进行索引,*PMD+(index[30:21]*8),MMU从PMD表项中读取PTE表的基地址;
7,MMU检查PMD目录项的有效性(bit[1:0]),以及其属性标志判断是否允许请求的内存访问。假设它有效,且允许访问内存;
8,pmd目录表项中获取bit[39:12],作为pte页表的物理基基址,
9,pte指向一个4k的页(page descriptor),mmu获取pte的bit[39:12]作为最终物理地址的pa[39:12];
10,取出va[11:0]作为pa[11:0],然后返回完整的PA[39:0],以及来自页表项的附加信息。
四、实例分析
1,基于carsh_arm64(fulldump)测试代码流程
????????本例程创建一个/proc/paging_test节点,通echo命令将虚拟地址va和pid两个参数传入内核,然后调用下面的函数,输出对应的页目录以及页表内容,可以根据页表输出的地址和虚拟地址的偏移相加得到最终的物理地址,最后echo "0" > /proc/paging_test 造成死机,获取fulldump,使用crash(crash有命令可以将虚拟地址转换为物理地址)工具验证我们使用测试代码计算的结论的正确性;
static int paging_test_init(void)
{
//unsigned long pa = 0; //pa表示的物理地址
struct task_struct *pcb_tmp = NULL;
unsigned long Kernel_mask = 0xffff000000000000;
pgd_t *pgd_tmp = NULL;
pud_t *pud_tmp = NULL;
pmd_t *pmd_tmp = NULL;
pte_t *pte_tmp = NULL;
struct pid *p = NULL;
ppa = __virt_to_phys(va);
printk(KERN_INFO"virt_to_phys is = %p\n",ppa);
printk(KERN_INFO"PAGE_OFFSET = 0x%lx\n",PAGE_OFFSET); //页表中有多少个项
printk(KERN_INFO"PGDIR_SHIFT = %d\n",PGDIR_SHIFT);
printk(KERN_INFO"PUD_SHIFT = %d\n",PUD_SHIFT);
printk(KERN_INFO"PMD_SHIFT = %d\n",PMD_SHIFT);
printk(KERN_INFO"PAGE_SHIFT = %d\n",PAGE_SHIFT);
printk(KERN_INFO"PTRS_PER_PGD = %d\n",PTRS_PER_PGD); //每个PGD里面有多少个ptrs
printk(KERN_INFO"PTRS_PER_PUD = %d\n",PTRS_PER_PUD);
printk(KERN_INFO"PTRS_PER_PMD = %d\n",PTRS_PER_PMD); //PMD中有多少个项
printk(KERN_INFO"PTRS_PER_PTE = %d\n",PTRS_PER_PTE);
printk(KERN_INFO"PAGE_MASK = 0x%lx\n",PAGE_MASK); //页的掩码
if(va&Kernel_mask){ // 地址高位掩码区分内核和用户地址以使用不同的pgd全局目录表
printk(KERN_INFO"kernel virtual address!!!init_mm.pgd = %lx\n",init_mm.pgd);
pgd_tmp = pgd_offset(&init_mm,va);
}else{
p = find_vpid(pid);
pcb_tmp = pid_task(p,PIDTYPE_PID);
printk(KERN_INFO"user virtual address!!!\n pgd = %lx\n",pcb_tmp->mm->pgd);
if(!find_vma(pcb_tmp->mm,va)){
printk(KERN_INFO"virt_addr 0x%lx not available.\n",va);
return 0;
}
pgd_tmp = pgd_offset(pcb_tmp->mm,va);
}
printk(KERN_INFO"pgd_tmp = 0x%lx\n",pgd_tmp);
printk(KERN_INFO"pgd_val(*pgd_tmp) = 0x%lx\n",pgd_val(*pgd_tmp));
if(pgd_none(*pgd_tmp)){
printk(KERN_INFO"Not mapped in pgd.\n");
//return 0;
}
pud_tmp = pud_offset(pgd_tmp,va); //返回va对应的页上级目录项的线性地址
printk(KERN_INFO"pud_tmp = 0x%lx\n",pud_tmp);
printk(KERN_INFO"pud_val(*pud_tmp) = 0x%lx\n",pud_val(*pud_tmp));
if(pud_none(*pud_tmp)){
printk(KERN_INFO"pud Not mapped in pud.\n");
//return 0;
}
pmd_tmp = pmd_offset(pud_tmp,va); //返回va在页中间目录中对应表项的线性地址
printk(KERN_INFO"pmd_tmp = 0x%lx\n",pmd_tmp);
printk(KERN_INFO"pmd_val(*pmd_tmp) = 0x%lx\n",pmd_val(*pmd_tmp));
if(pmd_none(*pmd_tmp)){
printk(KERN_INFO"pmd Not mapped in pmd.\n");
// return 0;
}
pte_tmp = pte_offset_kernel(pmd_tmp,va); //pte指的是 找到表
printk(KERN_INFO"pte_tmp = 0x%lx\n",pte_tmp);
printk(KERN_INFO"pte_val(*pte_tmp) = 0x%lx\n",pte_val(*pte_tmp));
if(pte_none(*pte_tmp)){ //判断有没有映射
printk(KERN_INFO"pte Not mapped in pte.\n");
// return 0;
}
if(!pte_present(*pte_tmp)){
printk(KERN_INFO"pte not in RAM.\n");
// return 0;
}
//pa = (pte_val(*pte_tmp) & PAGE_MASK) ;//物理地址的计算方法
//printk(KERN_INFO"virt_addr 0x%lx in RAM Page is 0x%lx .\n",va,pa);
//printk(KERN_INFO"contect in 0x%lx is 0x%lx\n",pa,*(unsigned long *)((char *)pa + PAGE_OFFSET));
return 0;
}
2,内核虚拟地址的转换举例
内核虚拟地址:0xffffff800a185452
取自附录2表地址范围,由于内核地址只有一个pgd页表,所以pid可以随意填写,
adb shell "echo \"ffffff800a185452 1234\" > /proc/paging_test"
?执行上面命令后,抓取log如下:
代码log输出
[ 764.050574] userbuf = ffffff800a185452 1234; { \\bui
[ 764.050589] pid = 1234
[ 764.050596] virtual address = ffffff800a185452
[ 764.050604] virt_to_phys is = 000000003fc812c1
[ 764.050609] PAGE_OFFSET = 0xffffffc000000000
[ 764.050615] PGDIR_SHIFT = 30
[ 764.050619] PUD_SHIFT = 30
[ 764.050624] PMD_SHIFT = 21
[ 764.050629] PAGE_SHIFT = 12
[ 764.050635] PTRS_PER_PGD = 512
[ 764.050639] PTRS_PER_PUD = 1
[ 764.050644] PTRS_PER_PMD = 512
[ 764.050648] PTRS_PER_PTE = 512
[ 764.050653] PAGE_MASK = 0xfffffffffffff000
[ 764.050659] kernel virtual address!!!init_mm.pgd = ffffff8009edb000
[ 764.050665] pgd_tmp = 0xffffff8009edb000
[ 764.050670] pgd_val(*pgd_tmp) = 0xffffd003
[ 764.050675] pud_tmp = 0xffffff8009edb000
[ 764.050680] pud_val(*pud_tmp) = 0xffffd003
[ 764.050684] pmd_tmp = 0xffffffc07fffd280
[ 764.050689] pmd_val(*pmd_tmp) = 0xfab09003
[ 764.050694] pte_tmp = 0xffffffc07ab09c28
[ 764.050699] pte_val(*pte_tmp) = 0x680000a00c470f
手动转换步骤?:
(由于为3级页表pud和pgd相等)内核页表基地址为init_mm.pgd?= (其类型pgd_t*)0xffffff8009edb000
#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
#define pgd_offset_raw(pgd, addr) ((pgd) + pgd_index(addr))
#define pgd_offset(mm, addr) (pgd_offset_raw((mm)->pgd, (addr)))
/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(addr) pgd_offset(&init_mm, addr) //kernel 直接使用init_mm
根据以上宏可以计算的pgd_tmp =?0xffffff8009edb000(其类型为pgd_t*) +0x000(如上图的pgd_index?)=0xffffff8009edb000;
pgd_val(*pgd_tmp) =?0xffffd003;? //由2.2节可知,pgd只能指向下一个页表的基地址,且bit[1:0] ={1,1} ,为有效项,?因此0xffffd003为下一个页表的基地址,
#define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
#define pmd_offset_phys(dir, addr) ?(pud_page_paddr(*(dir)) + pmd_index(addr) * sizeof(pmd_t))
#define pmd_offset(dir, addr) ((pmd_t *)__va(pmd_offset_phys((dir), (addr))))
继续计算pmd_tmp,先计算 0xffffd000(4k对齐)?+0x280 然后使用__va宏将其转换为线性地址,就得到了pmd_tmp =?__va(0xffffd280) =?0xffffffc07fffd280;
pmd_val(*pmd_tmp) = 0xfab09003, 因为bit[1:0] ={1,1}这个表项是一个table entry,指向下一个页表的地址
#define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
#define pte_offset_phys(dir,addr) (pmd_page_paddr(READ_ONCE(*(dir))) + pte_index(addr) * sizeof(pte_t))
#define pte_offset_kernel(dir,addr) ((pte_t *)__va(pte_offset_phys((dir), (addr))))
继续计算pte_tmp,同样的方法 pte_tmp =?__va(0xfab09000+0xc28) =?0xffffffc07ab09c28
根据2.2节的介绍,pte只能指向物理地址,不能指向下一个表项,
pte_val(*pte_tmp) = 0x680000a00c470f
因此可以得到物理地址为:
Physical address?= a00c4000 + 0x452(offset) = 0xa00c4452
结果:virtulal address? 0xffffff800a185452 对应的物理地址为?0xa00c4452?
细心的读者应该发现了一个问题,就是pgd在计算pgd_temp的时候pgd_index没有乘8,也就是没乘以sizeof(pgd_t),如下所示:
#define pgd_offset_raw(pgd, addr) ? ((pgd) + pgd_index(addr)) #define pmd_offset_phys(dir, addr) ?(pud_page_paddr(*(dir)) + pmd_index(addr) * sizeof(pmd_t)) #define pte_offset_phys(dir,addr) ? (pmd_page_paddr(READ_ONCE(*(dir))) + pte_index(addr) * sizeof(pte_t))
但是如果找一个pgd_index不为0的堆/栈虚拟地址,计算会发现结果是已经乘过8的,其实它是通过地址的形式隐式的乘了8:
#define pgd_offset_raw(pgd, addr) ? ((pgd) + pgd_index(addr))
?注意pgd的类型 pgd == init_mm.pgd == &(init_mm)->pgd 三个变量类型均为pgd_t *型,(pmd,pte则都为一个数据类型,并不是地址),其为一个64位的地址类型,占8个字节,因此(pgd_t *类型)pgd+n?== (char *)pgd+?n*8,因此在计算pgd表内偏移的时候,其实是默认乘了8的。
接着执行以下命令制造死机,然后抓取fulldump,
adb shell "echo 0 >/proc/paging_test"
使用crash_arm64工具解析dump,并验证我们之前的计算结果如下:
?还可以继续验证之前计算过程中的这两个等式:
pmd_tmp =?__va(0xffffd280) =?0xffffffc07fffd280;
pte_tmp =?__va(0xfab09000+0xc28) =?0xffffffc07ab09c28
?显然,结论与我们计算完全一致!!!
3,内核PMD映射举例
? ? ? ? 在2.2节,我们讲述了巨页的概念,也就是pud,pmd表项可以是一个内存页地址,这里以pmd为例,这时其表项对应的bit[1:0]={0,1};pte不会在被映射,所以页大小被扩展为2^(9+12) = 2M,可以从附录2 表的第三列看到这个映射的例子,在附录2中随机选取一个PMD映射的例子,可以看到该地址映射的范围均为2m对齐,
0xffffffc001600000
0xffffffc001600000-0xffffffc002000000 10M PMD RW NX SHD AF BLK UXN
并使用crash_arm64工具直接转换为物理地址如下:
?由上图转换结果可知,*PMD = 81600711 其bit[1:0] = {0,1} 所以81600000就是该虚拟地址对应的物理地址,因此pte项不映射,且2m对齐,
pud映射也是类似的方法,这里不再赘述。?
4,用户虚拟地址的转换举例
用户虚拟地址:?0x30f8123
1,获取进程zygote 的进程pid = 417
XXXXXX:/ # ps -ef | grep zygote
root 417 1 4 16:56:08 ? 00:00:04 zygote
webview_zygote 1542 417 0 16:56:19 ? 00:00:00 webview_zygote
root 6739 6653 6 16:58:13 pts/0 00:00:00 grep zygote
2,获取zygote进程空间的映射表(完整表见附件zygote_maps.txt)
adb shell "cat /proc/417/maps" > zygote_maps.txt
...
030f8000-030f9000 rw-p 00004000 fd:04 769 /system/bin/app_process32
...
12c00000-32c00000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
...
?maps为zygote进程本身代码和相关资源在虚拟内存中的映射表,这里的虚拟地址就是zygote进程的虚拟地址;
在以上映射表中选取一个有效范围内的虚拟地址为:0x30f8123 由于是用户地址,需要task本身的pgd表,因此需要指定正确的pid,用来获取进程本身的pgd表基地址
adb shell echo "\"00000000030f8123 417\" > /proc/paging_test"
代码输出:
[ 774.076860] userbuf = 00000000030f8123 417
? ; { \\bui
[ 774.076886] pid = 417
[ 774.076903] virtual address = 30f8123
[ 774.076921] virt_to_phys is = 0000000022d92271
[ 774.076934] PAGE_OFFSET = 0xffffffc000000000
[ 774.076947] PGDIR_SHIFT = 30
[ 774.076959] PUD_SHIFT = 30
[ 774.076972] PMD_SHIFT = 21
[ 774.076984] PAGE_SHIFT = 12
[ 774.076998] PTRS_PER_PGD = 512
[ 774.077011] PTRS_PER_PUD = 1
[ 774.077024] PTRS_PER_PMD = 512
[ 774.077036] PTRS_PER_PTE = 512
[ 774.077049] PAGE_MASK = 0xfffffffffffff000
[ 774.077065] user virtual address!!!
pgd = ffffffc074637000
[ 774.077083] pgd_tmp = 0xffffffc074637000
[ 774.077097] pgd_val(*pgd_tmp) = 0xf4667003
[ 774.077110] pud_tmp = 0xffffffc074637000
[ 774.077123] pud_val(*pud_tmp) = 0xf4667003
[ 774.077136] pmd_tmp = 0xffffffc0746670c0
[ 774.077149] pmd_val(*pmd_tmp) = 0xf47f8003
[ 774.077162] pte_tmp = 0xffffffc0747f87c0
[ 774.077175] pte_val(*pte_tmp) = 0xe0000094096fd3
内核地址和用户地址转换的流程除了pgd的获取方式,其他基本相同,这里简单叙述如下:
这里的pgd不再是init_mm.pgd,而是通过pid 获取zygote 进程描述符,进而获取task_struct.mm.pgd为:
pgd_tmp = 0xfc06533a000
与内核地址相同的索引计算方法计算得出各个索引表索引为:
pgd_index = 0x0
pmd_index = 0xc0
pte_index = 7c0
offset = 0x123
pgd_tmp =??0xffffffc06533a000+0x0 =?0xffffffc06533a000
pmd_tmp = __va (pgd_val(*pgd_tmp) + 0xc0) = __va(0xdef670c0) = xffffffc05ef670c0
pte_tmp = __va(pmd_val(*pmd_tmp)+0x7c0) =?__va(0xccdc57c0) = 0xffffffc04cdc57c0
pte_val指向一个地址项??0xe0000094096bd3 因此可以得出物理地址为:
phy address = 0x94096000 +0x123(offset) = 0x94096123
user virtulal address? 0x30f8123 对应的物理地址为?0x94096123
同样使用crash_arm64 验证结论,以及上面的两个等式如下:
ps:注意crash_arm64的vtop命令用来吧虚拟地址转换为物理地址,其默认使用的是kernel的pgd表,因此在计算用户虚拟地址的时候,需要指定对应的pid,用以获取对应进程的pgd表;ptov是用来吧物理地址转换为虚拟地址,由于物理地址是唯一的,因此在ptov时是不需要指定pid的)
ps:本来有一个很好的例子,就是通过驱动模块的方式,使用两个参数分别为pid,和虚拟地址的值,在加载ko的时候使用不同的虚拟地址作为参数,但是编译总是报init_mm(内核全局变量)未定义,折腾了大半天,最后查到init_mm ?is not exported past 2.6.29, on the basis that no out-of-tree code should be using it. ,也就是驱动模块的方式是无法访问init_mm,swapper_pg_dir的,只能编译进内核,但是编译进内核又无法传参,太麻烦!
????????因此最后选择了proc 节点的方式,其实也可以通过字符设备驱动的方式传入参数,但是相比于驱动,proc节点更为简单,由于echo 只能传入字符串,自己写了个字符串到整形地址转换的接口,效果还算可以!!
????????还尝试了使用qemu用做例子,因为qemu足够迅速便捷,还能单步调试内核代码,但是qemu调试内核使用的gdb是不支持吧虚拟地址转换为物理地址的命令的,因此 ,使用qemu只能计算,并不能验证结论,因此qemu也只能pass了。
? ? ? ?经历了十几个日夜,终于是搞完了着一篇,?时间也过的真快,转眼十一都过完了,希望一切都好,加油。。。
五,参考文档
《深入理解linux内核》
《armv8_a_address_translation.pdf》
《DDI0487_I_a_a-profile_architecture_reference_manual》
《DEN0024A_v8_architecture_PG》
《learn_the_architecture_-_aarch64_memory_management_101811_0102_00_en》
《gdb.pdf》
六,附录:
附录1
1,kernel 内存布局
[ 0.000000] Memory: 1832624K/2097084K available (15356K kernel code, 2116K rwdata, 6188K rodata, 4736K init, 2648K bss, 264460K reserved, 0K cma-reserved)
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] modules : 0xffffff8000000000 - 0xffffff8008000000 ( 128 MB)
[ 0.000000] vmalloc : 0xffffff8008000000 - 0xffffffbebfff0000 ( 250 GB)
[ 0.000000] .text : 0xffffff8008080000 - 0xffffff8008f80000 ( 15360 KB)
[ 0.000000] .rodata : 0xffffff8008f80000 - 0xffffff8009590000 ( 6208 KB)
[ 0.000000] .init : 0xffffff8009590000 - 0xffffff8009a30000 ( 4736 KB)
[ 0.000000] .data : 0xffffff8009a30000 - 0xffffff8009c41000 ( 2116 KB)
[ 0.000000] .bss : 0xffffff8009c41000 - 0xffffff8009ed7130 ( 2649 KB)
[ 0.000000] fixed : 0xffffffbefe7fb000 - 0xffffffbefec00000 ( 4116 KB)
[ 0.000000] PCI I/O : 0xffffffbefee00000 - 0xffffffbeffe00000 ( 16 MB)
[ 0.000000] vmemmap : 0xffffffbf00000000 - 0xffffffc000000000 ( 4 GB maximum)
[ 0.000000] 0xffffffbf00000000 - 0xffffffbf01ffffc0 ( 31 MB actual)
[ 0.000000] memory : 0xffffffc000000000 - 0xffffffc07ffff000 ( 2047 MB)
[ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
[ 0.000000] ftrace: allocating 37750 entries in 148 pages
附录2
adb shell cat /sys/kernel/debug/kernel_page_tables > kernel_page_tables1409.txt
---[ Modules start ]---
0xffffff8000f80000-0xffffff8000f8b000 44K PTE ro x SHD AF UXN MEM/NORMAL
0xffffff8000f8b000-0xffffff8000f8d000 8K PTE ro NX SHD AF UXN MEM/NORMAL
0xffffff8000f8d000-0xffffff8000f94000 28K PTE RW NX SHD AF UXN MEM/NORMAL
0xffffff8000f9e000-0xffffff8000fb0000 72K PTE ro x SHD AF UXN MEM/NORMAL
0xffffff8000fb0000-0xffffff8000fb5000 20K PTE ro NX SHD AF UXN MEM/NORMAL
0xffffff8000fb5000-0xffffff8000fbe000 36K PTE RW NX SHD AF UXN MEM/NORMAL
0xffffff8000fca000-0xffffff8000fcd000 12K PTE ro x SHD AF UXN MEM/NORMAL
0xffffff8000fcd000-0xffffff8000fce000 4K PTE ro NX SHD AF UXN MEM/NORMAL
...
0xffffffbebfbeb000-0xffffffbebfbfa000 60K PTE RW NX SHD AF UXN MEM/NORMAL
0xffffffbebfc00000-0xffffffbebfff0000 4032K PTE RW NX SHD AF UXN MEM/NORMAL
---[ vmalloc() End ]---
---[ Fixmap start ]---
0xffffffbefe7fc000-0xffffffbefe7fd000 4K PTE ro x SHD AF UXN MEM/NORMAL
0xffffffbefe7fd000-0xffffffbefe7fe000 4K PTE ro NX SHD AF UXN MEM/NORMAL
0xffffffbefe7ff000-0xffffffbefe800000 4K PTE RW NX SHD AF UXN DEVICE/nGnRE
0xffffffbefe800000-0xffffffbefea00000 2M PMD ro NX SHD AF BLK UXN MEM/NORMAL
---[ Fixmap end ]---
---[ PCI I/O start ]---
---[ PCI I/O end ]---
---[ vmemmap start ]---
0xffffffbf00000000-0xffffffbf02000000 32M PMD RW NX SHD AF BLK UXN MEM/NORMAL
---[ vmemmap end ]---
---[ Linear Mapping ]---
0xffffffc000000000-0xffffffc000080000 512K PTE RW NX SHD AF CON UXN MEM/NORMAL
0xffffffc000080000-0xffffffc000200000 1536K PTE ro NX SHD AF UXN MEM/NORMAL
0xffffffc000200000-0xffffffc001400000 18M PMD ro NX SHD AF BLK UXN MEM/NORMAL
0xffffffc001400000-0xffffffc001590000 1600K PTE ro NX SHD AF UXN MEM/NORMAL
0xffffffc001590000-0xffffffc001600000 448K PTE RW NX SHD AF CON UXN MEM/NORMAL
0xffffffc001600000-0xffffffc002000000 10M PMD RW NX SHD AF BLK UXN MEM/NORMAL
0xffffffc002000000-0xffffffc004000000 32M PMD RW NX SHD AF CON BLK UXN MEM/NORMAL
0xffffffc004000000-0xffffffc005200000 18M PMD RW NX SHD AF BLK UXN MEM/NORMAL
0xffffffc005200000-0xffffffc0053f0000 1984K PTE RW NX SHD AF CON UXN MEM/NORMAL
0xffffffc005400000-0xffffffc006000000 12M PMD RW NX SHD AF BLK UXN MEM/NORMAL
0xffffffc006000000-0xffffffc07e000000 1920M PMD RW NX SHD AF CON BLK UXN MEM/NORMAL
0xffffffc07e000000-0xffffffc07fe00000 30M PMD RW NX SHD AF BLK UXN MEM/NORMAL
0xffffffc07fe00000-0xffffffc07fff0000 1984K PTE RW NX SHD AF CON UXN MEM/NORMAL
0xffffffc07fff0000-0xffffffc07ffff000 60K PTE RW NX SHD AF UXN MEM/NORMAL
附录3
?adb shell "cat /proc/417/maps" > zygote_maps.txt
030f1000-030f3000 r--p 00000000 fd:04 769 /system/bin/app_process32
030f3000-030f7000 r-xp 00001000 fd:04 769 /system/bin/app_process32
030f7000-030f8000 r--p 00004000 fd:04 769 /system/bin/app_process32
030f8000-030f9000 rw-p 00004000 fd:04 769 /system/bin/app_process32
030f9000-030fa000 rw-p 00000000 00:00 0 [anon:.bss]
12c00000-32c00000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
6fb2d000-6fd06000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot.art]
6fd06000-6fd49000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot-core-libart.art]
6fd49000-6fde8000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot-core-icu4j.art]
6fde8000-6fe11000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot-okhttp.art]
6fe11000-6fe47000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot-bouncycastle.art]
6fe47000-6fe52000 rw-p 00000000 00:00 0 [anon:dalvik-/apex/com.android.art/javalib/boot-apache-xml.art]
6fe52000-6fed0000 r--p 00000000 fd:04 105 /apex/com.android.art/javalib/arm/boot.oat
6fed0000-700dc000 r-xp 0007e000 fd:04 105 /apex/com.android.art/javalib/arm/boot.oat
700dc000-700dd000 rw-p 00000000 00:00 0 [anon:.bss]
700dd000-700df000 r--p 00000000 fd:04 106 /apex/com.android.art/javalib/arm/boot.vdex
700df000-700e0000 r--p 0028a000 fd:04 105 /apex/com.android.art/javalib/arm/boot.oat
700e0000-700e1000 rw-p 0028b000 fd:04 105 /apex/com.android.art/javalib/arm/boot.oat
700e1000-700f0000 r--p 00000000 fd:04 99 /apex/com.android.art/javalib/arm/boot-core-libart.oat
700f0000-70130000 r-xp 0000f000 fd:04 99 /apex/com.android.art/javalib/arm/boot-core-libart.oat
70130000-70131000 rw-p 00000000 00:00 0 [anon:.bss]
70131000-70132000 r--p 00000000 fd:04 100 /apex/com.android.art/javalib/arm/boot-core-libart.vdex
70132000-70133000 r--p 0004f000 fd:04 99 /apex/com.android.art/javalib/arm/boot-core-libart.oat
70133000-70134000 rw-p 00050000 fd:04 99 /apex/com.android.art/javalib/arm/boot-core-libart.oat
70134000-7015e000 r--p 00000000 fd:04 96 /apex/com.android.art/javalib/arm/boot-core-icu4j.oat
7015e000-701fb000 r-xp 0002a000 fd:04 96 /apex/com.android.art/javalib/arm/boot-core-icu4j.oat
701fb000-701fc000 rw-p 00000000 00:00 0 [anon:.bss]
701fc000-701fd000 r--p 00000000 fd:04 97 /apex/com.android.art/javalib/arm/boot-core-icu4j.vdex
701fd000-701fe000 r--p 000c7000 fd:04 96 /apex/com.android.art/javalib/arm/boot-core-icu4j.oat
701fe000-701ff000 rw-p 000c8000 fd:04 96 /apex/com.android.art/javalib/arm/boot-core-icu4j.oat
701ff000-7020a000 r--p 00000000 fd:04 102 /apex/com.android.art/javalib/arm/boot-okhttp.oat
7020a000-70231000 r-xp 0000b000 fd:04 102 /apex/com.android.art/javalib/arm/boot-okhttp.oat
...
cbe26000-cbe27000 r--p 00000000 fd:04 425 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.0.so
cbe27000-cbe28000 r-xp 00000000 fd:04 425 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.0.so
cbe28000-cbe29000 r--p 00000000 fd:04 425 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.0.so
cbe5f000-cbe60000 r--p 00000000 fd:05 944 /vendor/lib/arm.graphics-V1-ndk_platform.so
cbe60000-cbe61000 r-xp 00000000 fd:05 944 /vendor/lib/arm.graphics-V1-ndk_platform.so
cbe61000-cbe62000 r--p 00000000 fd:05 944 /vendor/lib/arm.graphics-V1-ndk_platform.so
cbe8e000-cbe98000 r--p 00000000 fd:05 965 /vendor/lib/hw/android.hardware.graphics.mapper@4.0-impl-arm.so
cbe98000-cbea9000 r-xp 00009000 fd:05 965 /vendor/lib/hw/android.hardware.graphics.mapper@4.0-impl-arm.so
cbea9000-cbeab000 r--p 00019000 fd:05 965 /vendor/lib/hw/android.hardware.graphics.mapper@4.0-impl-arm.so
cbeab000-cbeac000 rw-p 0001a000 fd:05 965 /vendor/lib/hw/android.hardware.graphics.mapper@4.0-impl-arm.so
cbeac000-cbead000 rw-p 00000000 00:00 0 [anon:.bss]
cbee1000-cbee6000 r--p 00000000 fd:04 455 /apex/com.android.vndk.v30/lib/libcutils.so
cbee6000-cbeec000 r-xp 00004000 fd:04 455 /apex/com.android.vndk.v30/lib/libcutils.so
cbeec000-cbeee000 r--p 00009000 fd:04 455 /apex/com.android.vndk.v30/lib/libcutils.so
cbeee000-cbeef000 rw-p 0000a000 fd:04 455 /apex/com.android.vndk.v30/lib/libcutils.so
cbf2a000-cbf2b000 r--p 00000000 fd:04 427 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.2.so
cbf2b000-cbf2c000 r-xp 00000000 fd:04 427 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.2.so
cbf2c000-cbf2d000 r--p 00000000 fd:04 427 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common@1.2.so
cbf42000-cbf4c000 r--p 00000000 fd:04 448 /apex/com.android.vndk.v30/lib/libbase.so
cbf4c000-cbf6b000 r-xp 00009000 fd:04 448 /apex/com.android.vndk.v30/lib/libbase.so
cbf6b000-cbf6d000 r--p 00027000 fd:04 448 /apex/com.android.vndk.v30/lib/libbase.so
cbf6d000-cbf6e000 rw-p 00028000 fd:04 448 /apex/com.android.vndk.v30/lib/libbase.so
cbf86000-cbf96000 r--p 00000000 fd:04 467 /apex/com.android.vndk.v30/lib/libprocessgroup.so
cbf96000-cbfb2000 r-xp 0000f000 fd:04 467 /apex/com.android.vndk.v30/lib/libprocessgroup.so
cbfb2000-cbfb3000 r--p 0002a000 fd:04 467 /apex/com.android.vndk.v30/lib/libprocessgroup.so
cbfb3000-cbfb4000 rw-p 0002a000 fd:04 467 /apex/com.android.vndk.v30/lib/libprocessgroup.so
cbfca000-cbffb000 r--p 00000000 fd:04 461 /apex/com.android.vndk.v30/lib/libhidlbase.so
cbffb000-cc047000 r-xp 00030000 fd:04 461 /apex/com.android.vndk.v30/lib/libhidlbase.so
cc047000-cc04d000 r--p 0007b000 fd:04 461 /apex/com.android.vndk.v30/lib/libhidlbase.so
cc04d000-cc04e000 rw-p 00080000 fd:04 461 /apex/com.android.vndk.v30/lib/libhidlbase.so
cc09f000-cc0aa000 r--p 00000000 fd:04 431 /apex/com.android.vndk.v30/lib/android.hardware.graphics.mapper@4.0.so
cc0aa000-cc0b8000 r-xp 0000a000 fd:04 431 /apex/com.android.vndk.v30/lib/android.hardware.graphics.mapper@4.0.so
cc0b8000-cc0ba000 r--p 00017000 fd:04 431 /apex/com.android.vndk.v30/lib/android.hardware.graphics.mapper@4.0.so
cc0ba000-cc0bb000 rw-p 00018000 fd:04 431 /apex/com.android.vndk.v30/lib/android.hardware.graphics.mapper@4.0.so
cc0eb000-cc0ec000 r--p 00000000 fd:04 463 /apex/com.android.vndk.v30/lib/libion.so
cc0ec000-cc0ee000 r-xp 00000000 fd:04 463 /apex/com.android.vndk.v30/lib/libion.so
cc0ee000-cc0ef000 r--p 00001000 fd:04 463 /apex/com.android.vndk.v30/lib/libion.so
cc0ef000-cc0f0000 rw-p 00001000 fd:04 463 /apex/com.android.vndk.v30/lib/libion.so
cc13a000-cc13c000 r--p 00000000 fd:04 424 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common-V1-ndk_platform.so
cc13c000-cc13e000 r-xp 00001000 fd:04 424 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common-V1-ndk_platform.so
cc13e000-cc13f000 r--p 00002000 fd:04 424 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common-V1-ndk_platform.so
cc13f000-cc140000 rw-p 00002000 fd:04 424 /apex/com.android.vndk.v30/lib/android.hardware.graphics.common-V1-ndk_platform.so
cc159000-cc15b000 r--p 00000000 fd:04 2004 /system/framework/oat/arm/android.test.base.odex
cc15b000-cc15c000 r--p 00000000 fd:04 2005 /system/framework/oat/arm/android.test.base.vdex
cc15c000-cc15d000 r--p 00002000 fd:04 2004 /system/framework/oat/arm/android.test.base.odex
cc15d000-cc15e000 rw-p 00003000 fd:04 2004 /system/framework/oat/arm/android.test.base.odex
cc18e000-cc190000 r--p 00000000 fd:04 2002 /system/framework/oat/arm/android.hidl.manager-V1.0-java.odex
cc190000-cc191000 r--p 00000000 fd:04 2003 /system/framework/oat/arm/android.hidl.manager-V1.0-java.vdex
cc191000-cc192000 r--p 00002000 fd:04 2002 /system/framework/oat/arm/android.hidl.manager-V1.0-java.odex
cc192000-cc193000 rw-p 00003000 fd:04 2002 /system/framework/oat/arm/android.hidl.manager-V1.0-java.odex
cc1cd000-cc1cf000 r--p 00000000 fd:04 2000 /system/framework/oat/arm/android.hidl.base-V1.0-java.odex
cc1cf000-cc1d0000 r--p 00000000 fd:04 2001 /system/framework/oat/arm/android.hidl.base-V1.0-java.vdex
cc1d0000-cc1d1000 r--p 00002000 fd:04 2000 /system/framework/oat/arm/android.hidl.base-V1.0-java.odex
cc1d1000-cc1d2000 rw-p 00003000 fd:04 2000 /system/framework/oat/arm/android.hidl.base-V1.0-java.odex
...
e6ae7000-e6ae8000 rw-p 00049000 fd:04 2173 /system/lib/android.hardware.drm@1.0.so
e6ae8000-e6ae9000 r--s 00002000 fd:07 393 /product/overlay/GmsConfigOverlayGalleryGo.apk
e6ae9000-e6aee000 rw-p 00000000 00:00 0 [anon:libc_malloc]
e6aee000-e6b06000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
...
eb716000-eb72f000 r--p 00000000 fd:04 368 /apex/com.android.runtime/bin/linker
eb72f000-eb7b6000 r-xp 00018000 fd:04 368 /apex/com.android.runtime/bin/linker
eb7b6000-eb7ba000 r--p 0009e000 fd:04 368 /apex/com.android.runtime/bin/linker
eb7ba000-eb7bc000 rw-p 000a1000 fd:04 368 /apex/com.android.runtime/bin/linker
eb7bc000-eb7bf000 rw-p 00000000 00:00 0 [anon:.bss]
eb7bf000-eb7c0000 r--p 00000000 00:00 0 [anon:.bss]
eb7c0000-eb7c6000 rw-p 00000000 00:00 0 [anon:.bss]
ebad6000-ebad7000 ---p 00000000 00:00 0 [anon:dalvik-Sentinel fault page]
ff45c000-ff45d000 ---p 00000000 00:00 0
ff45d000-ffc5c000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
附录4
物理地址范围
adb shell cat /sys/kernel/debug/memblock/memory > memory1409.txt
0: 0x0000000080000000..0x00000000853effff
1: 0x0000000085400000..0x00000000ffffefff
|