/proc/<pid>/pagemap
пример минимального запуска для пользователя
virt_to_phys_user.c:
#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */
typedef struct {
uint64_t pfn : 54;
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;
uintptr_t vpn;
vpn = vaddr / sysconf(_SC_PAGE_SIZE);
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, &data, sizeof(data) - nread,
vpn * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 54) - 1);
entry->soft_dirty = (data >> 54) & 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)
{
pid_t pid;
uintptr_t vaddr, paddr = 0;
if (argc < 3) {
printf("Usage: %s pid vaddr\n", argv[0]);
return EXIT_FAILURE;
}
pid = strtoull(argv[1], NULL, 0);
vaddr = strtoull(argv[2], NULL, 0);
if (virt_to_phys_user(&paddr, pid, vaddr)) {
fprintf(stderr, "error: virt_to_phys_user\n");
return EXIT_FAILURE;
};
printf("0x%jx\n", (uintmax_t)paddr);
return EXIT_SUCCESS;
}
GitHub upstream .
Использование:
sudo ./virt_to_phys_user.out <pid> <physical-address>
sudo
требуется для чтения /proc/<pid>/pagemap
, даже если у вас есть разрешения на доступ к файлам, как описано в: https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838
Как указано в: https://stackoverflow.com/a/46247716/895245Linux распределяет таблицы страниц лениво, поэтому убедитесь, что вы прочитали и записали байт по этому адресу из тестовой программы, прежде чем использовать virt_to_phys_user
.
Как проверить это
Тестовая программа:
#define _XOPEN_SOURCE 700
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
enum { I0 = 0x12345678 };
static volatile uint32_t i = I0;
int main(void) {
printf("vaddr %p\n", (void *)&i);
printf("pid %ju\n", (uintmax_t)getpid());
while (i == I0) {
sleep(1);
}
printf("i %jx\n", (uintmax_t)i);
return EXIT_SUCCESS;
}
Тестовая программа выводит адрес принадлежащей ей переменной и ее PID, например:
vaddr 0x600800
pid 110
, и затем вы можете передать преобразование виртуального адресас помощью:
sudo ./virt_to_phys_user.out 110 0x600800
Наконец, преобразование можно проверить с помощью /dev/mem
для наблюдения / изменения памяти, но вы не можете сделать это в Ubuntu 17.04 без перекомпиляции ядра, так как для этого требуется: CONFIG_STRICT_DEVMEM=n
см. также: Как получить доступ к физическим адресам из пространства пользователя в Linux? Buildroot - это простой способ чтобы преодолеть это, однако.
В качестве альтернативы, вы можете использовать виртуальную машину, например, команду xp
монитора QEMU: Как декодировать записи / proc / pid / pagemap в Linux?
См. Это для вывода всех страниц: Как декодировать записи / proc / pid / pagemap в Linux?
Пользовательское подмножество этого вопроса: Как найтифизический адрес переменной из пространства пользователя в Linux?
Дамп всех страниц процесса с /proc/<pid>/maps
/proc/<pid>/maps
перечисляет все диапазоны адресовпроцесс, так что мы можем пройти, чтобы перевести все страницы: / proc / [pid] / pagemaps и / proc / [pid] / maps |linux
Kerneland virt_to_phys
работает только для kmalloc
адресов
Из модуля ядра, virt_to_phys
, упоминалось.
Однако важно подчеркнуть, что у него есть это ограничение.
Например, оно не работает для переменных модуля.arc/x86/include/asm/io.h
документация:
Возвращенный физический адрес - это физическое (CPU) отображение для заданного адреса памяти.Допустимо использовать эту функцию только для адресов, напрямую сопоставленных или выделенных с помощью kmalloc.
Вот модуль ядра, который иллюстрирует это вместе с тестом пользовательского пространства .
Так что это не очень общая возможность.См .: Как получить физический адрес из логического адреса в модуле ядра Linux? исключительно для методов модуля ядра.