so true

心怀未来,开创未来!
随笔 - 160, 文章 - 0, 评论 - 40, 引用 - 0
数据加载中……

virtual addr to physical addr

转自: http://www.cnblogs.com/pengdonglin137/p/6802108.html
顺便提一句, 通过man 5 proc可以查看proc下各个文件的介绍情况,但由于pagemap是后加的,man里查不到目前
利用/proc/pid/pagemap将虚拟地址转换为物理地址
内核文档: Documentation/vm/pagemap.txt
pagemap is a new (as of 2.6.25) set of interfaces in the kernel that allow
userspace programs to examine the page tables and related information by
reading files in /proc.
There are four components to pagemap:
 * /proc/pid/pagemap.  This file lets a userspace process find out which
   physical frame each virtual page is mapped to.  It contains one 64-bit
   value for each virtual page, containing the following data (from
   fs/proc/task_mmu.c, above pagemap_read):
    * Bits 0-54  page frame number (PFN) if present
    * Bits 0-4   swap type if swapped
    * Bits 5-54  swap offset if swapped
    * Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
    * Bit  56    page exclusively mapped (since 4.2)
    * Bits 57-60 zero
    * Bit  61    page is file-page or shared-anon (since 3.5)
    * Bit  62    page swapped
    * Bit  63    page present
   Since Linux 4.0 only users with the CAP_SYS_ADMIN capability can get PFNs.
   In 4.0 and 4.1 opens by unprivileged fail with -EPERM.  Starting from
   4.2 the PFN field is zeroed if the user does not have CAP_SYS_ADMIN.
   Reason: information about PFNs helps in exploiting Rowhammer vulnerability.
   If the page is not present but in swap, then the PFN contains an
   encoding of the swap file number and the page's offset into the
   swap. Unmapped pages return a null PFN. This allows determining
   precisely which pages are mapped (or in swap) and comparing mapped
   pages between processes.
   Efficient users of this interface will use /proc/pid/maps to
   determine which areas of memory are actually mapped and llseek to
   skip over unmapped regions.
 下面是一个工具:
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <assert.h>
 5 #include <errno.h>
 6 #include <stdint.h>
 7 #include <string.h>
 8 
 9 #define PAGEMAP_ENTRY 8
10 #define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
11 #define GET_PFN(X) X & 0x7FFFFFFFFFFFFF
12 
13 const int __endian_bit = 1;
14 #define is_bigendian() ( (*(char*)&__endian_bit) == 0 )
15 
16 int i, c, pid, status;
17 unsigned long virt_addr; 
18 uint64_t read_val, file_offset, page_size;
19 char path_buf [0x100] = {};
20 FILE * f;
21 char *end;
22 
23 int read_pagemap(char * path_buf, unsigned long virt_addr);
24 
25 int main(int argc, char ** argv){
26     if(argc!=3){
27         printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
28         return -1;
29     }
30     if(!memcmp(argv[1],"self",sizeof("self"))){
31         sprintf(path_buf, "/proc/self/pagemap");
32         pid = -1;
33     }
34     else{
35         pid = strtol(argv[1],&end, 10);
36         if (end == argv[1] || *end != '\0' || pid<=0){ 
37             printf("PID must be a positive number or 'self'\n");
38             return -1;
39         }
40     }
41     virt_addr = strtoll(argv[2], NULL, 16);
42     if(pid!=-1)
43         sprintf(path_buf, "/proc/%u/pagemap", pid);
44 
45     page_size = getpagesize();
46     read_pagemap(path_buf, virt_addr);
47     return 0;
48 }
49 
50 int read_pagemap(char * path_buf, unsigned long virt_addr){
51     printf("Big endian? %d\n", is_bigendian());
52     f = fopen(path_buf, "rb");
53     if(!f){
54         printf("Error! Cannot open %s\n", path_buf);
55         return -1;
56     }
57 
58     //Shifting by virt-addr-offset number of bytes
59     //and multiplying by the size of an address (the size of an entry in pagemap file)
60     file_offset = virt_addr / page_size * PAGEMAP_ENTRY;
61     printf("Vaddr: 0x%lx, Page_size: %lld, Entry_size: %d\n", virt_addr, page_size, PAGEMAP_ENTRY);
62     printf("Reading %s at 0x%llx\n", path_buf, (unsigned long long) file_offset);
63     status = fseek(f, file_offset, SEEK_SET);
64     if(status){
65         perror("Failed to do fseek!");
66         return -1;
67     }
68     errno = 0;
69     read_val = 0;
70     unsigned char c_buf[PAGEMAP_ENTRY];
71     for(i=0; i < PAGEMAP_ENTRY; i++){
72         c = getc(f);
73         if(c==EOF){
74             printf("\nReached end of the file\n");
75             return 0;
76         }
77         if(is_bigendian())
78             c_buf[i] = c;
79         else
80             c_buf[PAGEMAP_ENTRY - i - 1] = c;
81         printf("[%d]0x%x ", i, c);
82     }
83     for(i=0; i < PAGEMAP_ENTRY; i++){
84         //printf("%d ",c_buf[i]);
85         read_val = (read_val << 8) + c_buf[i];
86     }
87     printf("\n");
88     printf("Result: 0x%llx\n", (unsigned long long) read_val);
89     if(GET_BIT(read_val, 63)) {
90         uint64_t pfn = GET_PFN(read_val);
91         printf("PFN: 0x%llx (0x%llx)\n", pfn, pfn * page_size + virt_addr % page_size);
92     } else
93         printf("Page not present\n");
94     if(GET_BIT(read_val, 62))
95         printf("Page swapped\n");
96     fclose(f);
97     return 0;
98 }
测试:
用Qemu+vexpress-ca9:
内存: 1GB, 物理地址范围: 0x60000000->0x9FFFFFFF
通过查看/proc/pid/maps获得进程的地址空间的内存映射情况:
 1 [root@vexpress ~]# cat /proc/746/maps 
 2 00008000-001f3000 r-xp 00000000 b3:01 62         /bin/busybox
 3 001fa000-001fc000 rw-p 001ea000 b3:01 62         /bin/busybox
 4 001fc000-00222000 rw-p 00000000 00:00 0          [heap]
 5 b6c7f000-b6c80000 rw-p 00000000 00:00 0 
 6 b6c80000-b6c8d000 r-xp 00000000 b3:01 174        /lib/libnss_files-2.18.so
 7 b6c8d000-b6c94000 ---p 0000d000 b3:01 174        /lib/libnss_files-2.18.so
 8 b6c94000-b6c95000 r--p 0000c000 b3:01 174        /lib/libnss_files-2.18.so
 9 b6c95000-b6c96000 rw-p 0000d000 b3:01 174        /lib/libnss_files-2.18.so
10 b6c96000-b6ca1000 r-xp 00000000 b3:01 141        /lib/libnss_nis-2.18.so
11 b6ca1000-b6ca8000 ---p 0000b000 b3:01 141        /lib/libnss_nis-2.18.so
12 b6ca8000-b6ca9000 r--p 0000a000 b3:01 141        /lib/libnss_nis-2.18.so
13 b6ca9000-b6caa000 rw-p 0000b000 b3:01 141        /lib/libnss_nis-2.18.so
14 b6caa000-b6daa000 rw-p 00000000 00:00 0 
15 b6daa000-b6dca000 r-xp 00000000 b3:01 129        /lib/ld-2.18.so
16 b6dca000-b6dd1000 ---p 00020000 b3:01 129        /lib/ld-2.18.so
17 b6dd1000-b6dd2000 r--p 0001f000 b3:01 129        /lib/ld-2.18.so
18 b6dd2000-b6dd3000 rw-p 00020000 b3:01 129        /lib/ld-2.18.so
19 b6dd3000-b6f06000 r-xp 00000000 b3:01 170        /lib/libc-2.18.so
20 b6f06000-b6f0d000 ---p 00133000 b3:01 170        /lib/libc-2.18.so
21 b6f0d000-b6f0f000 r--p 00132000 b3:01 170        /lib/libc-2.18.so
22 b6f0f000-b6f10000 rw-p 00134000 b3:01 170        /lib/libc-2.18.so
23 b6f10000-b6f13000 rw-p 00000000 00:00 0 
24 b6f13000-b6f26000 r-xp 00000000 b3:01 177        /lib/libnsl-2.18.so
25 b6f26000-b6f2d000 ---p 00013000 b3:01 177        /lib/libnsl-2.18.so
26 b6f2d000-b6f2e000 r--p 00012000 b3:01 177        /lib/libnsl-2.18.so
27 b6f2e000-b6f2f000 rw-p 00013000 b3:01 177        /lib/libnsl-2.18.so
28 b6f2f000-b6f31000 rw-p 00000000 00:00 0 
29 b6f31000-b6f39000 r-xp 00000000 b3:01 154        /lib/libnss_compat-2.18.so
30 b6f39000-b6f40000 ---p 00008000 b3:01 154        /lib/libnss_compat-2.18.so
31 b6f40000-b6f41000 r--p 00007000 b3:01 154        /lib/libnss_compat-2.18.so
32 b6f41000-b6f42000 rw-p 00008000 b3:01 154        /lib/libnss_compat-2.18.so
33 be958000-be979000 rw-p 00000000 00:00 0          [stack]
34 bed04000-bed05000 r-xp 00000000 00:00 0          [sigpage]
35 bed05000-bed06000 r--p 00000000 00:00 0          [vvar]
36 bed06000-bed07000 r-xp 00000000 00:00 0          [vdso]
37 ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]
可以看看0x8000这个虚拟地址对应的物理地址:
1 [root@vexpress ~]# ./translate 746 0x8000
2 Big endian? 0
3 Vaddr: 0x8000, Page_size: 4096, Entry_size: 8
4 Reading /proc/746/pagemap at 0x40
5 [0]0x0 [1]0xf8 [2]0x9 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa0 
6 Result: 0xa00000000009f800
7 PFN: 0x9f800 (0x9f800000)
可以看到, 对应的物理页帧是0x9F800,那么物理地址就是0x9F800000.
下面我们再做一个实验, 进程746的地址空间有一部分用来映射libc:
1 b6dd3000-b6f06000 r-xp 00000000 b3:01 170        /lib/libc-2.18.so
2 b6f06000-b6f0d000 ---p 00133000 b3:01 170        /lib/libc-2.18.so
3 b6f0d000-b6f0f000 r--p 00132000 b3:01 170        /lib/libc-2.18.so
4 b6f0f000-b6f10000 rw-p 00134000 b3:01 170        /lib/libc-2.18.so
此外, 进程835也会用到libc:
1 [root@vexpress ~]# cat /proc/835/maps 
2 ... ...
3 b6e0b000-b6f3e000 r-xp 00000000 b3:01 170        /lib/libc-2.18.so
4 b6f3e000-b6f45000 ---p 00133000 b3:01 170        /lib/libc-2.18.so
5 b6f45000-b6f47000 r--p 00132000 b3:01 170        /lib/libc-2.18.so
6 b6f47000-b6f48000 rw-p 00134000 b3:01 170        /lib/libc-2.18.so
7 ... ...
可以看到, 进程746和835虽然都用了libc,但是对应的虚拟地址却不同,前者是0xb6dd3000, 而后者是0xb6e0b000, 我们知道对于共享库, 在内存只会存在一份代码, 那么物理地址也就是唯一的(代码段是唯一的,所有调用libc的进程共享,而数据段每个进程一个), 那么进程746的虚拟地址空间的0xb6dd3000(代码段)跟进程835的虚拟地址空间的0xb6e0b000(代码段)对应的物理地址应该是同一个, 下面验证一下:
进程746:
1 [root@vexpress ~]# ./translate 746 0xb6dd3000
2 virt_addr: 0xb6dd3000
3 Big endian? 0
4 Vaddr: 0xb6dd3000, Page_size: 4096, Entry_size: 8
5 Reading /proc/746/pagemap at 0x5b6e98
6 [0]0x68 [1]0xfa [2]0x9 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa0 
7 Result: 0xa00000000009fa68
8 PFN: 0x9fa68 (0x9fa68000)
可以看到,物理地址是0x9FA68000
进程835:
1 [root@vexpress ~]# ./translate 835 0xb6e0b000
2 virt_addr: 0xb6e0b000
3 Big endian? 0
4 Vaddr: 0xb6e0b000, Page_size: 4096, Entry_size: 8
5 Reading /proc/835/pagemap at 0x5b7058
6 [0]0x68 [1]0xfa [2]0x9 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa0 
7 Result: 0xa00000000009fa68
8 PFN: 0x9fa68 (0x9fa68000)
可以看到, 物理地址也是0x9FA68000, 从而证明了我们的猜想。
=========================================================================
转自:http://blog.sina.com.cn/s/blog_628cc2b70101c8zu.html
pagemap是linux中一组新的接口集合,他通过读取/proc中的文件允许用户态的程序检查页表以及相关的信息。
它主要有3个组成部分:
(1)/proc/pid/pagemap:这个文件允许一个用户态的进程查看到每个虚拟页映射到的物理页,每一个虚拟页都包含了一个64位的值,信息如下:
Bits 0-54: page frame number(PFN) if present
Bits 0-4:  swap type if swapped
Bits 5-54: swap offset if swapped
Bits 55-60:page shift 
Bit 61:   reserved ofr future use
Bit 62:   page swapped
Bit 63:   page present
    如果这个page是在swap状态,然后PFN包含一个编码的交换文件号码,再将页的offset值写入swap中。没有映射的页就返回一个null PFN。这样就可以精确判断一个page是映射的或是swap的,并且可以比较不同进程间的映射页。
    我们可以利用/proc/pid/maps去判断内存中哪块区域是被映射的,然后通过llseek就可以跳过没有映射的区域。
(2)/proc/kpagecount:这个文件包含一个64位值,该值表示每个page被映射的次数,通过PFN索引。
(3)/proc/kpageflags:这个文件包含每一个page的64位的标记集,通过PFN索引。
通过pagemap查看一个进程的内存使用情况的一般步骤如下:
(1)读取/proc/pid/maps文件确定内存空间的哪个部分被映射;
(2)选择你感兴趣的maps,比如全部、一部分或者堆栈什么的;
(3)打开/proc/pid/pagemap,定位到你准备去检查的pages;
(4)通过pagemap读取每一个page的64位值;
(5)打开/proc/kpagecount以及/proc/kpageflags。对于每一个PFN你只要去读,定位到文件中的那个入口,然后读取你想要的数据。

posted on 2017-08-17 17:33 so true 阅读(194) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: