Skip to content

Instantly share code, notes, and snippets.

@rumpelsepp
Last active October 26, 2021 16:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rumpelsepp/6a87bcee01939243f4b83fa5bd9a7d40 to your computer and use it in GitHub Desktop.
Save rumpelsepp/6a87bcee01939243f4b83fa5bd9a7d40 to your computer and use it in GitHub Desktop.
Read the pagemap of a given process id
// Public Domain; Feel free to copy and steal it!
// Based on:
// http://fivelinesofcode.blogspot.de/2014/03/how-to-translate-virtual-to-physical.html
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#define PM_ENTRY_BYTES 8
#define PM_PFRAME_BITS 55
#define PM_PFRAME_MASK ((1LL << PM_PFRAME_BITS) - 1)
#define PM_PFRAME(x) ((x) & PM_PFRAME_MASK)
#define MAX_SWAPFILES_SHIFT 5
#define PM_SWAP_OFFSET(x) (((x) & PM_PFRAME_MASK) >> MAX_SWAPFILES_SHIFT)
#define PM_SOFT_DIRTY (1ULL << 55)
#define PM_MMAP_EXCLUSIVE (1ULL << 56)
#define PM_FILE (1ULL << 61)
#define PM_SWAP (1ULL << 62)
#define PM_PRESENT (1ULL << 63)
int str2i(int *buf, char *str, int base) {
char *endptr;
errno = 0;
*buf = (int) strtol(str, &endptr, base);
if (errno == ERANGE || *endptr != '\0' || endptr == str) {
return -1;
}
return 0;
}
int str2ul(unsigned long *buf, char *str, int base) {
char *endptr;
errno = 0;
*buf = strtoul(str, &endptr, base);
if (errno == ERANGE || *endptr != '\0' || endptr == str) {
return -1;
}
return 0;
}
int read_pagemap(char *pmpath, unsigned long vaddr){
FILE *fd;
off_t offset;
uint64_t read_val;
int r;
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
printf("Big endian\n");
#else
printf("Little endian\n");
#endif
fd = fopen(pmpath, "rb");
if (fd == NULL) {
perror("fopen(3)");
return -1;
}
// Shifting by virt-addr-offset number of bytes
// and multiplying by the size of an address (the size of an entry in pagemap file)
offset = (vaddr / getpagesize()) * PM_ENTRY_BYTES;
printf("Vaddr: 0x%lx, Page_size: %d, Entry_size: %d\n", vaddr, getpagesize(), PM_ENTRY_BYTES);
printf("Reading %s at 0x%llx\n", pmpath, (unsigned long long) offset);
r = fseek(fd, offset, SEEK_SET);
if (r < 0) {
perror("fseek(3)");
return -1;
}
// TODO: Endianess?
fread(&read_val, PM_ENTRY_BYTES, 1, fd);
if (ferror(fd)) {
perror("fread(3)");
return -1;
}
printf("\n");
printf("Result: 0x%llx\n", (unsigned long long) read_val);
if (read_val & PM_PRESENT) {
printf("PFN: 0x%llx\n", (unsigned long long) PM_PFRAME(read_val));
} else {
printf("Page not present\n");
}
if (read_val & PM_SWAP) {
printf("Page swapped\n");
}
fclose(fd);
return 0;
}
int main(int argc, char **argv){
char pmpath[PATH_MAX] = { 0 };
unsigned long vaddr;
pid_t pid;
int r;
if (argc != 3) {
fprintf(stderr, "usage: %s PID VADDR\n", argv[0]);
goto fail;
}
if (strcmp(argv[1], "self") == 0) {
pid = getpid();
} else {
r = str2i(&pid, argv[1], 10);
if (r < 0 || pid < 0) {
fprintf(stderr, "PID must be a positive number or 'self'\n");
goto fail;
}
}
r = str2ul(&vaddr, argv[2], 16);
if (r < 0) {
fprintf(stderr, "Could not parse VADDR\n");
goto fail;
}
sprintf(pmpath, "/proc/%u/pagemap", pid);
r = read_pagemap(pmpath, vaddr);
if (r < 0) {
goto fail;
}
return EXIT_SUCCESS;
fail:
return EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment