We all know that processes running in Linux acts only in virtual address space. So whenever a process wants to access a data (okay datum) it requests CPU for a virtual address. The CPU intern converts it into physical address and fetches the data. It will be nice to have a program that converts virtual address to physical address, won’t it?
Linux from 2.5.26 provides a proc interface, pagemap that contains information what we want. Each process has its pagemap at /proc/p_id/pagemap. According to the Documentation it is a binary file contains a sequence of 64-bit words. Each word contains information regarding one virtual page for full virtual address space. Among them bits 0-54 (55-bits) represents the address of the physical frame number (PFN). I think that’s all we need. Adding the offset of a variable from virtual page address to the PFN will give us the physical memory address.
WARNING: Don’t try to read the pagemap file directly. cat /proc/self/pagemap or vim /proc/p_id/pagemap is not going to return anytime soon.
We’ll write a small C program and the let’s try to get physical address of a variable used in that C program. As the PFN data will be present only if the data is not moved to swap, lets use mlock() to lock the memory in physical memory.
/* lock the allocated memory in RAM */ mlock(ptr, MEM_LENGTH);
/* print the pid and vaddr. Thus we can work on him */ printf("my pid: %d\n\n", getpid()); printf("virtual address to work: 0x%lx\n", (unsignedlong)ptr);
/* make the program to wait for user input */ scanf("%c", &ptr[16]);
return0; }
Run the specimen.c program, get its p_id and start the dissection.
1 2 3 4 5
$ gcc specimen.c -o specimen $ ./specimen my pid: 11953
virtual address to work: 0x55cd75821260
In a 64-bit machine, virtual address-space is from 0x00 and to 2^64 - 1. First we have to calculate the page offset for the given virtual address [find on which virtual memory page, the address resides]. And multiply that with 8 as each virtual page table has 8-byte information word in the pagemap file.
Now cursor is on the first byte of 64-bit word containing the information we need. According to the Documentation bits 0-54 represents the physical page frame number (PFN). So read 7-bytes and discard most significant bit.
This is the PFN. Add offset of the virtual address from its virtual page base address to the page shifted PFN to get the physical address of the memory.
$ sudo ./a.out 11953 0x55cd75821260 getting page number of virtual address 94340928115296 of process 11953 opening pagemap /proc/11953/pagemap moving to 184259625224 physical frame address is 0x20508 physical address is 0x20508260