內核必須懂(七): Linux四級頁表(x64)

目錄

  • 前言
  • Intel四級頁表
  • 實操尋址
  • 獲取cr3
  • 獲取PGD
  • 獲取PUD
  • 獲取PMD
  • 獲取PTE
  • 獲取內容
  • 最後

前言

Linux四級頁表的做用主要就是地址映射, 將邏輯地址映射到物理地址. 不少時候, 有些地方想不明白就能夠查看實際物理地址進行分析.bash


Intel 四級頁表

其實不少設計的根源或者說緣由都來自於CPU的設計, OS不少時候都是輔助CPU. Linux的四級頁表就是依據CPU的四級頁表來設計的. 這裏主要說的就是Intel x64頁面大小爲4KB的狀況, 如圖所示:工具

固然, 你能夠用指令確認下:ui

getconf PAGE_SIZE 
複製代碼


實操尋址

首先這裏先貼出幾個工具, fileview, dram, registers. 這些都是能夠幫助快速獲取地址的, 上一篇文章說的kgdb工具也是能夠的, 就是麻煩一點, 你懂的. 具體內容就不貼了, 這裏僅展現用戶態的代碼:google

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#define BUFSIZE 4096

int main()
{
	int			fd, ret;
	char			buf[BUFSIZE];
	unsigned long int	a = 0x1234567890abcdef;
	printf( "a=0x%016lX addr: %p \n", a, &a );

	if ( (fd = open( "/proc/registers", O_RDONLY ) ) < 0 )
	{
		fprintf( stderr, "Open /proc/registers file failed! \n" );
		exit( EXIT_FAILURE );
	}

	lseek( fd, 0L, SEEK_SET );

	if ( (ret = read( fd, buf, sizeof buf - 1 ) ) < 0 )
	{
		perror( "/proc/registers" );
		exit( EXIT_FAILURE );
	}

	buf[ret] = 0;
	close( fd );
	puts( buf );

	while ( 1 );
	return(0);
}
複製代碼

使用make指令編譯工具, 插入dram.ko和registers.ko驅動模塊. 編譯運行用戶態程序, 如圖所示:spa


獲取cr3

這之中最關鍵的是cr3地址以及局部變量地址, 這裏看到, 變量地址是0x7ffdcbffaba8, 變量值是0x1234567890ABCDEF. cr3寄存器中地址是0x40c78000. 固然了, 按照CPU的圖示, cr3確定是指向PML4E. 在Linux當中, 第一級頁表稱爲PGD, 固然是有歷史緣由的, 能夠自行google. 因此Linux的四級頁表分別是PGD -> PUD -> PMD -> PTE.設計


獲取PGD

想要獲取PGD中的內容須要經過計算. 這裏先來處理一下局部變量地址. 首先寫成二進制.3d

0x7ffdcbffaba8
0111 1111 1111 1101 1100 1011 1111 1111 1010 1011 1010 1000
複製代碼

而後按照Intel的設計, 從新整合.code

011111111 111110111 001011111 111111010 101110101000
複製代碼

從新寫成16進制:cdn

ff 1f7 5f 1fa ba8
複製代碼

這就是隻要用的offset. 由於每一個單元是64-bits所以須要在序號基礎上乘以8得到地址. 因此PGD地址爲:blog

0x40c78000(cr3) + ff * 8 = 0x40c787f8
複製代碼

而後使用啓動以前編譯的小工具:

./fileview /dev/dram
複製代碼

輸入以前計算出來的地址0x40c787f8, 就能夠獲得之中的內容, 也就是PUD, 從CPU圖來講就是PDPTE:


獲取PUD

這裏獲取到的是67 50 75 76 00 00 00 80, 可是注意, Intel是和顯示順序反過來的. 也就是76755067, 而後後面的12-bits是頁面屬性. 因此, 具體地址就是:

76755000 + 1f7 * 8 = 76755fb8
複製代碼

一樣輸入地址到工具, 獲得67 80 E8 2C 00 00 00 00.


獲取PMD

直接計算了:

2ce88000 + 5f * 8 = 2ce882f8
複製代碼

獲得67 00 DD 48 00 00 00 00.


獲取PTE

直接計算了:

48dd0000 + 1fa * 8 = 48dd0fd0
複製代碼

獲得67 58 59 20 00 00 00 80.

獲取內容

最後就能夠獲取到內容了:

20595000 + ba8 = 20595ba8
複製代碼


最後

固然了, 此次是在用戶態下進行從線性地址到物理地址轉換的, 若是是內核態有些地方會發生變化. 暫時寫到這裏, 內核態等後續的更新了. 喜歡記得點贊, 有意見或者建議評論區見~

相關文章
相關標籤/搜索