/proc/[pid]/pagemaps and /proc/[pid]/maps | linux /proc/[pid]/pagemaps and /proc/[pid]/maps | linux c c

/proc/[pid]/pagemaps and /proc/[pid]/maps | linux


F**/proc/<pid>/pagemap + /proc/<pid>/maps dump example program**

Here is a pagemap example that converts virtual addresses to physical: Is there any API for determining the physical address from virtual address in Linux?

The following program uses both /proc/<pid>/pagemap + /proc/<pid>/maps to dump page table information to show how they can be used together. Usage:

sudo ./pagemap_dump.out <pid>

Sample output:

addr pfn soft-dirty file/shared swapped present library400000 12845d 0 1 0 1 /bin/bash401000 12845e 0 1 0 1 /bin/bash402000 12845f 0 1 0 1 /bin/bash

This tells us for example that the virtual address 0x400000 maps to the physical address0x12845d000.

Why sudo is required: https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838

This program works in two steps:

  • parse the human readable lines lines from /proc/<pid>/maps. This files contains lines of form:

    7ffff7b6d000-7ffff7bdd000 r-xp 00000000 fe:00 658                        /lib/libuClibc-1.0.22.so

    which gives us:

    • 7f8af99f8000-7f8af99ff000: a virtual address range that belong to the process, possibly containing multiple pages.
    • /lib/libuClibc-1.0.22.so the name of the library that owns that memory.
  • loop over each page of each address range, and ask /proc/<pid>/pagemap for more information about that page, including the physical address.

pagemap_dump.c

#define _XOPEN_SOURCE 700#include <errno.h>#include <fcntl.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>typedef struct {    uint64_t pfn : 55;    unsigned int soft_dirty : 1;    unsigned int file_page : 1;    unsigned int swapped : 1;    unsigned int present : 1;} PagemapEntry;/* Parse the pagemap entry for the given virtual address. * * @param[out] entry      the parsed entry * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file * @param[in]  vaddr      virtual address to get entry for * @return 0 for success, 1 for failure */int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr){    size_t nread;    ssize_t ret;    uint64_t data;    nread = 0;    while (nread < sizeof(data)) {        ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread,                (vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);        nread += ret;        if (ret <= 0) {            return 1;        }    }    entry->pfn = data & (((uint64_t)1 << 55) - 1);    entry->soft_dirty = (data >> 55) & 1;    entry->file_page = (data >> 61) & 1;    entry->swapped = (data >> 62) & 1;    entry->present = (data >> 63) & 1;    return 0;}/* Convert the given virtual address to physical using /proc/PID/pagemap. * * @param[out] paddr physical address * @param[in]  pid   process to convert for * @param[in] vaddr virtual address to get entry for * @return 0 for success, 1 for failure */int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr){    char pagemap_file[BUFSIZ];    int pagemap_fd;    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);    pagemap_fd = open(pagemap_file, O_RDONLY);    if (pagemap_fd < 0) {        return 1;    }    PagemapEntry entry;    if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {        return 1;    }    close(pagemap_fd);    *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));    return 0;}int main(int argc, char **argv){    char buffer[BUFSIZ];    char maps_file[BUFSIZ];    char pagemap_file[BUFSIZ];    int maps_fd;    int offset = 0;    int pagemap_fd;    pid_t pid;    if (argc < 2) {        printf("Usage: %s pid\n", argv[0]);        return EXIT_FAILURE;    }    pid = strtoull(argv[1], NULL, 0);    snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid);    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);    maps_fd = open(maps_file, O_RDONLY);    if (maps_fd < 0) {        perror("open maps");        return EXIT_FAILURE;    }    pagemap_fd = open(pagemap_file, O_RDONLY);    if (pagemap_fd < 0) {        perror("open pagemap");        return EXIT_FAILURE;    }    printf("addr pfn soft-dirty file/shared swapped present library\n");    for (;;) {        ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset);        if (length <= 0) break;        length += offset;        for (size_t i = offset; i < (size_t)length; i++) {            uintptr_t low = 0, high = 0;            if (buffer[i] == '\n' && i) {                const char *lib_name;                size_t y;                /* Parse a line from maps. Each line contains a range that contains many pages. */                {                    size_t x = i - 1;                    while (x && buffer[x] != '\n') x--;                    if (buffer[x] == '\n') x++;                    while (buffer[x] != '-' && x < sizeof buffer) {                        char c = buffer[x++];                        low *= 16;                        if (c >= '0' && c <= '9') {                            low += c - '0';                        } else if (c >= 'a' && c <= 'f') {                            low += c - 'a' + 10;                        } else {                            break;                        }                    }                    while (buffer[x] != '-' && x < sizeof buffer) x++;                    if (buffer[x] == '-') x++;                    while (buffer[x] != ' ' && x < sizeof buffer) {                        char c = buffer[x++];                        high *= 16;                        if (c >= '0' && c <= '9') {                            high += c - '0';                        } else if (c >= 'a' && c <= 'f') {                            high += c - 'a' + 10;                        } else {                            break;                        }                    }                    lib_name = 0;                    for (int field = 0; field < 4; field++) {                        x++;                        while(buffer[x] != ' ' && x < sizeof buffer) x++;                    }                    while (buffer[x] == ' ' && x < sizeof buffer) x++;                    y = x;                    while (buffer[y] != '\n' && y < sizeof buffer) y++;                    buffer[y] = 0;                    lib_name = buffer + x;                }                /* Get info about all pages in this page range with pagemap. */                {                    PagemapEntry entry;                    for (uintptr_t addr = low; addr < high; addr += sysconf(_SC_PAGE_SIZE)) {                        /* TODO always fails for the last page (vsyscall), why? pread returns 0. */                        if (!pagemap_get_entry(&entry, pagemap_fd, addr)) {                            printf("%jx %jx %u %u %u %u %s\n",                                (uintmax_t)addr,                                (uintmax_t)entry.pfn,                                entry.soft_dirty,                                entry.file_page,                                entry.swapped,                                entry.present,                                lib_name                            );                        }                    }                }                buffer[y] = '\n';            }        }    }    close(maps_fd);    close(pagemap_fd);    return EXIT_SUCCESS;}


Oooh K, the index was correct but comparing off64_t o (8bytes) with long index was interpreting o wrong hence why I was getting that error.Ha! this was a stupid mistake.So adding the appropriate header took care of that.

Missing header :-/ sigh fixes the issue of comparing off64_t with a unsigned long.