2010-03-13 5 views

Odpowiedz

2

Po pierwsze, dlaczego chcesz to zrobić? Celem nowoczesnych systemów VM jest usunięcie programisty aplikacji ze złożoności układu pamięci fizycznej. Przesyłając im każdą własną, jednolitą przestrzeń adresową, aby ułatwić im życie.

Jeśli chcesz to zrobić, to prawie musisz użyć modułu jądra. Uzyskaj wirtualny adres zmiennej w normalny sposób, użyj jej do zindeksowania w tabelach stron procesów i odczytania znalezionej wartości (fizyczny adres ramki). Następnie dodaj przesunięcie strony, aby uzyskać pełny adres fizyczny. Pamiętaj, że nie będziesz mógł używać tego adresu, gdy włączona jest funkcja stronicowania.

(Jeśli masz szczęście być może uda się uzyskać adres ramki regionu VM z systemu plików/proc, a tym samym nie byłby wymagają, aby napisać moduł jądra.)

+4

... i chyba że zablokujesz stronę w pamięci, aby adres fizyczny mógł się zmienić w dowolnym momencie. – caf

+1

Nie musisz pisać modułu jądra: jak wyjaśniają inne przykłady, jest to już ujawnione przez '/ proc/$ pid/pagemap'. – poolie

+0

W architekturze NUMA może być interesujące poznanie fizycznego adresu zmiennych – horro

-5

(edit: Jeśli przez „adres fizyczny ", masz na myśli poziom" w którym moduł RAM są moje bity przechowywane ", a następnie odpowiedź jest niewłaściwa.)

Nie trzeba uprawnień root'a, aby to zrobić. Zamiast tego potrzebujesz debuggera. I oto idziemy (używając systemu Linux na x86_64):

Najpierw potrzebujemy małego programu do zabawy. Ten uzyskuje dostęp do zmiennej globalnej i drukuje ją dwa razy z rzędu. Ma dwie zmienne globalne, które znajdujemy w pamięci później.

 
#include <stdio.h> 

int a, b = 0; 

int main(void) 
{ 
    printf("a: "); 
    if (fscanf("%d", &a) < 1) 
     return 0; 

    printf("a = %d\n", myglobal); 

    printf("b: "); 
    if (fscanf("%d", &b) < 1) 
     return 0; 

    printf("a = %d, b = %d\n", a, b); 

    return 0; 
} 

Krok 1: Kompilacja programu i rozebrać wszystkie informacje debugowania z niego, więc nie mamy żadnych wskazówek z debuggera, że ​​nie dostanie się w sytuacji prawdziwym życiu.

 
$ gcc -s -W -Wall -Os -o ab ab.c 

Krok 2: Uruchom program i wpisz jedną z dwóch liczb.

 
$ ./ab 
a: 123 
a = 123 
b: _ 

Krok 3: Znajdź proces.

 
$ ps aux | grep ab 
roland 21601 0.0 0.0 3648 456 pts/11 S+ 15:17 0:00 ./ab 
roland 21665 0.0 0.0 5132 672 pts/12 S+ 15:18 0:00 grep ab 

Krok 4: Dołącz debugger do procesu (21601).

 
$ gdb 
... 
(gdb) attach 21601 
... 
(gdb) where 
#0 0x00007fdecfdd2970 in read() from /lib/libc.so.6 
#1 0x00007fdecfd80b40 in _IO_file_underflow() from /lib/libc.so.6 
#2 0x00007fdecfd8230e in _IO_default_uflow() from /lib/libc.so.6 
#3 0x00007fdecfd66903 in _IO_vfscanf() from /lib/libc.so.6 
#4 0x00007fdecfd7245c in scanf() from /lib/libc.so.6 
#5 0x0000000000400570 in ??() 
#6 0x00007fdecfd2f1a6 in __libc_start_main() from /lib/libc.so.6 
#7 0x0000000000400459 in ??() 
#8 0x00007fffd827da48 in ??() 
#9 0x000000000000001c in ??() 
#10 0x0000000000000001 in ??() 
#11 0x00007fffd827f9a2 in ??() 
#12 0x0000000000000000 in ??() 

Ciekawa rama ma numer 5, ponieważ jest między jakiegoś kodu wywołującego funkcję main i funkcję scanf, więc to musi być nasz main funkcja. Kontynuując sesji debugowania:

 
(gdb) frame 5 
... 
(gdb) disassemble $pc $pc+50 
... 
0x0000000000400570 :  test %eax,%eax 
0x0000000000400572 :  jle 0x40058c <[email protected]+372> 
0x0000000000400574 :  mov 0x2003fe(%rip),%edx  # 0x600978 <[email protected]+2098528> 
0x000000000040057a :  mov 0x2003fc(%rip),%esi  # 0x60097c <[email protected]+2098532> 
0x0000000000400580 :  mov $0x40068f,%edi 
0x0000000000400585 :  xor %eax,%eax 
0x0000000000400587 :  callq 0x4003f8 <[email protected]> 
... 

Teraz wiemy, że funkcja printf dostanie trzy parametry, a dwie są tylko cztery bajty z dala od siebie. To dobry znak, że te dwie są naszymi zmiennymi a i b. Tak więc adres a to 0x600978 lub 0x60097c. Przekonajmy się, starając:

 
(gdb) x/w 0x60097c   
0x60097c <[email protected]+2098532>: 0x0000007b 
(gdb) x/w 0x600978 
0x600978 <[email protected]+2098528>: 0x00000000 

Więc a, zmienna, która jest odczytywana w pierwszym, jest pod adresem 0x60097c (bo 0x0000007B jest szesnastkowa reprezentacja 123, który weszliśmy) i b jest 0x600978.

Nadal w debugerze możemy teraz modyfikować zmienną a, a następnie kontynuować program.

 
(gdb) set *(int *)0x60097c = 1234567 
(gdb) continue 

Powrót w programie, który poprosił nas, by wprowadzić dwa numery:

 
$ ./ab 
a: 123 
a = 123 
b: 5 
a = 1234567, b = 5 
$ 
+11

Daje to Wirtualny adres, a nie fizyczne. –

16

jako częściowo odpowiedział przed normalne programy nie ma potrzeby martwić się o adresach fizycznych, jak uruchomić w wirtualnej przestrzeni adresowej z wszystkie jego udogodnienia. Ponadto nie każdy adres wirtualny ma fizyczny adres, może należeć do zamapowanych plików lub zamienionych stron. Jednak czasami może być interesujące zobaczenie tego mapowania, nawet w przestrzeni użytkownika.

W tym celu jądro Linux udostępnia swoje mapowanie do przestrzeni użytkownika za pośrednictwem zestawu plików w pliku /proc. Dokumentację można znaleźć pod numerem here. Krótkie podsumowanie:

  1. /proc/$pid/maps zawiera listę odwzorowań adresów wirtualnych wraz z dodatkowymi informacjami, takimi jak odpowiedni plik mapowanych plików.
  2. /proc/$pid/pagemap zawiera więcej informacji o każdej zmapowanej stronie, w tym adres fizyczny, jeśli istnieje.

This website dostarcza program w języku C, który zrzuca mapowania wszystkich uruchomionych procesów za pomocą tego interfejsu i wyjaśnia, co robi.

+0

Minimalny przykład z testami: https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141 –

14
#include "stdio.h" 
#include "unistd.h" 
#include "inttypes.h" 

uintptr_t vtop(uintptr_t vaddr) { 
    FILE *pagemap; 
    intptr_t paddr = 0; 
    int offset = (vaddr/sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); 
    uint64_t e; 

    // https://www.kernel.org/doc/Documentation/vm/pagemap.txt 
    if ((pagemap = fopen("/proc/self/pagemap", "r"))) { 
     if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) { 
      if (fread(&e, sizeof(uint64_t), 1, pagemap)) { 
       if (e & (1ULL << 63)) { // page present ? 
        paddr = e & ((1ULL << 54) - 1); // pfn mask 
        paddr = paddr * sysconf(_SC_PAGESIZE); 
        // add offset within page 
        paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); 
       } 
      } 
     } 
     fclose(pagemap); 
    } 

    return paddr; 
} 
Powiązane problemy