当前位置: 首页 > 科技观察

Linux虚拟内存地址转换为物理内存地址

时间:2023-03-12 09:52:51 科技观察

背景现代手机SOC(片上系统),由于功耗、Modem等功能在SOC上集成了很多核,它们也可以作为一个独立的系统运行。比如ADSP简介ADSP(ApplicationDigitalSignalProcessing)是高通公司的HexagonDSP,是一个独立运行的核心+系统。这样不仅可以用soc上的专用核来处理专业的东西,比如上面提到的ADSP可以处理音频解码,当然它的DSP特性也可以处理sensorfusion算法,相比通用处理器(cortexa72a53a17a9a8这些内核)处理更高效,更省电。当然,出于成本考虑,我们不会单独给它焊一个内存颗粒,它共享一部分主??内存,比如从地址0xc0000000-0xc01000001MB空间,此时内核(Linux运行在一个通用-目的处理器)将不再触摸这块内存。但是多核共享同一个地址空间也有一个缺点,就是如果程序出现问题(野指针,数组越界),可能会写入其他核管理的内存空间,这就给我们带来程序的值被莫名其妙地改变的问题。.为了排查此类问题,我们考虑将应用程序的虚拟地址转换为物理地址,并进行打印调试统一分析。实现内核在2.6.25增加了这样一个函数/proc/self/pagemap,即每个进程的/proc中都有一个pagemap,读取当前虚拟地址可以计算出对应的物理页里面的内容。然后加上page_offset就知道当前虚拟地址对应的物理地址了。pagemap要求您的应用程序具有root权限才能使用。#include#include#include#include#include#include#include#include#include#include#include#include//参考//https://www.kernel.org/doc/Documentation/vm/pagemap.txt#definepage_map_file"/proc/self/pagemap"#definePFN_MASK((((uint64_t)1)<<55)-1)#definePFN_PRESENT_FLAG(((uint64_t)1)<<63)intmem_addr_vir2phy(unsignedlongvir,unsignedlong*phy){intfd;intpage_size=getpagesize();unsignedlongvir_page_idx=vir/page_size;unsignedlongpfn_item_offset=vir_page_idx*sizeof(uint64_t);uint64_tpfn_item;fd=open(page_map_file,O_RDONLY);if(fd<0){fprintf(stderr,"open%sfailed",page_map_file);return-1;}if((off_t)-1==lseek(fd,pfn_item_offset,SEEK_SET)){fprintf(stderr,"lseek%sfailed",page_map_file);return-1;}if(sizeof(uint64_t)!=read(fd,&pfn_item,sizeof(uint64_t))){fprintf(stderr,"read%sfailed",page_map_file);return-1;}if(0==(pfn_item&PFN_PRESENT_FLAG)){fprintf(stderr,"pageisnotpresent");return-1;}*phy=(pfn_item&PFN_MASK)*page_size+vir%page_size;return0;}intmain(intargc,char*argv[]){unsignedlonga=0xffbbccaa;unsignedlongvir=reinterpret_cast(&a);unsignedlongphy=0;fprintf(stderr,"sizeof(unsignedlong):%lu,sizeof(unsignedlong*):%lu\n",sizeof(unsignedlong),sizeof(unsignedlong*));mem_addr_vir2phy(vir,&phy);fprintf(stderr,"1vir:0x%lx,phy:0x%lxgetchartocontinue\n",vir,phy);getchar();a=0x11111111;fprintf(stderr,"2vir:0x%lx,phy:0x%lxgetchartocontinue\n",vir,phy);getchar();fprintf(stderr,"3vir:0x%lx,phy:0x%lxa:0x%lx\n",vir,phy,a);}如何验证需要打开内核的以下模块CONFIG_DEVMEM=y关闭以下模块CONFIG_STRICT_DEVMEM=一般Android有命令/system/bin/r(源代码在system/core/toolbox/r.c),类似于devmem等嵌入式工具,通过/dev/mem读取(物理)memory)mmap物理内存的值,当然你也可以修改地址的值。在上面的例子中,它们通过getchar()来阻止程序运行,这样你就有足够的时间敲入/system/bin/r命令和参数命令的用法。在上面的例子中,我们将一个堆栈把上面变量的虚拟地址转换成物理地址,然后就可以通过/system/bin/r来读取和修改这个地址的值了。读取地址0x9a6f0b20的值adbshel??l/system/bin/r0x9a6f0b20修改地址0x9a6f0b20的值到0xffbbccaaadbshel??l/system/bin/r0x9a6f0b200xffbbccaa源码可以直接gitclonegit@github.com:green130181/kernel-study.git直接copypagemap在项目中到aosp的任意一个目录下然后在aosp的根目录下执行sourcebuild/envsetup.shlunch"yourselect"cdpagemapdirmm,然后adbpush到你的机器上,就可以开始验证了。当然还有很多高级的比如ramdump Trace32来实现内存地址检查,但是上面这些对于一个应用来说已经够轻量级了,够用了!