/proc/kcore失效,調試其文件系統相關模塊,使從新正常工做

爲分析內核,在有限的機器上用虛擬機裝了CentOS.6.9.i386.minimal,從新編譯了3.19.8內核並克隆。當使用/proc/kcore進行內核動態映像調試時,發現與kgdb遠程調試端讀到的內存數據不同。運行內核的測試機上的/proc/kcore裏面的數據大多都爲0,幾乎沒有一處用途。無論我進行多少次core-file去刷新/proc/kcore,結果也是無功而返。開始覺得gdb與新內核不兼容,但並非,用hexdump去讀取/proc/kcore的數據,只有開頭一小段是有數據跡象,其他都基本爲0。/proc/kcore大小約爲1G,內核arch爲x86。
新內核是按默認配置進行編譯,不知道是否其中有配置影響了/proc/kcore文件系統,因而我切換到隨CentOS.6.9發佈的內核2.6.32-696.el6.i686。結果出來了,在CentOS官方編譯的32位內核中,hexdump一樣只能夠讀到/proc/kcore第一個頁面大小的數據內容,當讀取到其他空間的內容時,只返回000100(也就是一個頁面的大小數值)。
那就可能跟編譯選項有關,因而找遍了menuconfig,和比較/boot目錄裏的config-$(uname -r),再參考kconfig裏面的參考說明,好像也沒有找到相關的選項。最後手段就是去調試文件系統下的kcore了。數據結構

kcore模塊的代碼在$YourSrcDir/fs/proc/kcore.c。先去閱讀代碼找出有用的函數做爲斷點切入分析。kcore.c文件不大,只有600不到700行,只要稍爲熟識文件系統就能夠分析。
kcore特殊文件只實現了(虛)文件系統中的 file_operations 接口,系統調用文件IO,映射相關的接口。app

static const struct file_operations proc_kcore_operations = {
    .read        = read_kcore,
    .open        = open_kcore,
    .llseek        = default_llseek,
};

kcore只爲文件系統提供了read, open, seek三種操做服務。也就是說咱們只能夠經過系統調用進行kcore的這三種操做。函數

下面是kcore.c模塊相關的數據結構和靜態變量:測試

// kcore.h
enum kcore_type {
    KCORE_TEXT,
    KCORE_VMALLOC,
    KCORE_RAM,
    KCORE_VMEMMAP,
    KCORE_OTHER,
};

struct kcore_list {
    struct list_head list;
    unsigned long addr;
    size_t size;
    int type;
};

struct vmcore {
    struct list_head list;
    unsigned long long paddr;
    unsigned long long size;
    loff_t offset;
};
// kcore.c模塊的靜態變量:
static LIST_HEAD(kclist_head);
static DEFINE_RWLOCK(kclist_lock);
static int kcore_need_update = 1;

 

這裏主要有兩類地址,KCORE_VMALLOC和KCORE_RAM。KCORE_RAM,在32位內核中內核地址與物理地址存在一種簡單的對應關係,內核地址=物理地址+3G,因此叫KCORE_RAM。可是3G到4G的地址空間中,也不是所有應用這一簡單映射關係的,還有一塊特殊的區域,就是KCORE_VMALLOC,它位於4G地址空間的最高256M,即0xf0000000到0xfffff000內。優化

VMALLOC這部分虛擬地址是用在ioremamp,也就是外部設備存儲單元使用到的I/O mapped地址。這區域的VMALLOC要與內存管理mm中的vm_area區分好。vmalloc是從內核地址空間,0xc0000000到high_memory(即VMALLOC_START - VMALLOC_OFFSET)爲止,進行虛擬空間分配,使用vm_struct結構和vmlist全局隊列管理。進程的內存空間由mm_struct結構和vm_area_struct結構進行管理。spa

// pgtable_32.c
unsigned long __FIXADDR_TOP = 0xfffff000;
EXPORT_SYMBOL(__FIXADDR_TOP);
// fixmap.h
#define FIXADDR_TOP    ((unsigned long)__FIXADDR_TOP)
#define FIXADDR_START        (FIXADDR_TOP - FIXADDR_SIZE)
#define FIXADDR_SIZE    (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
// pgtable_32_types.h
#define VMALLOC_START    ((unsigned long)high_memory + VMALLOC_OFFSET)
#ifdef CONFIG_HIGHMEM
# define VMALLOC_END    (PKMAP_BASE - 2 * PAGE_SIZE)
#else
# define VMALLOC_END    (FIXADDR_START - 2 * PAGE_SIZE)
#endif
#define MODULES_VADDR    VMALLOC_START
#define MODULES_END    VMALLOC_END
#define MODULES_LEN    (MODULES_VADDR - MODULES_END)

變量high_memory標誌着具體物理內存的上限所對應的虛擬地址,這是在系統初始化時設置好的。經過kgdb調試,查看到high_memory爲0xf0000000。調試

爲何個人內核只能夠讀到/proc/kcore的一個頁面的數據,其它地址上的數據全爲0呢,那麼參考讀操做函數read_kcore。
這個函數主要是將/proc/kcore文件尺寸的線性地址空間映射到不一樣的區域進行讀操做,與內存(文件)映射相似。
1.read_kcore將第一個頁面的地址空間的讀操做,映射爲一個ELF core header頭結構的數據內容。
2.從這個頭結構偏移後的地址,使用內核地址的簡單映射規則,進行映射,即 addr + 3G - sizeof(ELF core header)。
3.分別分派到kcore_list區域進行讀操做。
4.若是訪問地址知足is_vmalloc_or_module_addr,則進行vread,並copy_to_user。
5.若是訪問地址知足kern_addr_valid,則直接進行copy_to_user。
6.其他狀況clear_user。code

 

先來看kcore是如何將kcore文件的線性地址映射到內核地址的,下面的相關函數的反彙編代碼:blog

這裏要注意的是,代碼是被-O2優化過的,反彙編並不能與代碼很好地對應,開頭兩行多是承着某處的跳轉的。從行文來看%esi優化爲變量elf_buflen。接口

將圖的下面4行彙編逆向爲 0xc0000000 - elf_buflen + *fpos。 也就是 offset + 3G - sizeof(ELF core header)。

 

再來看kcore一共有幾個映射區間,只要walk through一下kclist_head隊列就能夠知道。本身寫一下調試腳本就能夠獲得。

kclist_head有兩個區間,地址區間與info files查看到映像區間一致。分別是0xf0800000開始的KCORE_VMALLOC區間,和0xc0000000開始的KCORE_RAM區間。 這兩個區間下面還會進行說明。

 

這就好辦了,斷點設置在read_kcore跑一次。結果發如今頭部分偏移以後的地址量的讀操做一切都被clear_user,也就是填0。對應高地址(即vmalloc,ioremap)的讀操做則是正常的。代碼已經被-O2優化過,用混合反彙編出來的代碼和彙編對應也是亂七八糟的。不過好在符號信息還在,那就設斷點到kern_addr_valid,卻發現找不到符號。通過彙編分析後發現,代碼在判斷is_vmalloc_or_module_addr失敗後直接進行clear_user。按代碼的邏輯應該是先進行kern_addr_valid判斷。也就是訪問地址不在最高 處的vamlloc,再判斷是否在內核有效的地址空間內。但實際的反彙編代碼卻根本找不到這個判斷邏輯。查閱kern_addr_valid在不一樣arch下實現後,發現x86下這個函數只是一個開關。

// arch/x86/include/asm/pgtable_32.h
#ifdef CONFIG_FLATMEM
#define kern_addr_valid(addr)    (1)
#else
#define kern_addr_valid(kaddr)    (0)
#endif

終於發如今arch x86中,內存管理模型影響了/proc/kcore特殊文件的運做。只有在FLAT模型下的內存管理,/proc/kcore才能提供正常的服務。內核中默認配置是使用SPARSE模型的,無論理是什麼arch的處理器。CentOS的32位內核也使用了這一默認配置,通過/boot/config-2.6.32-696.el6.i686覈實。

內核3.19.8從新進行menuconfig配置,選用FLAT內存管理模型,再次編譯後。OK!/proc/kcore特殊文件正常讀出內核內存映像的數據。x86 32位的內核必須使用FLAT內存管理模型,即CONFIG_FLATMEM_ENABLE=y,才能夠正常使用/proc/kcore。

在重編譯後,我懶得編譯modules,使用原來SPARSE內存模型編譯出來的modules進行安裝。安裝時提示許多mm_section符號缺失的warning,實際運行內核時也就杯具了。幸虧是虛擬機,回退從新所有編譯就是了,機器配置有限,至少就一個半小時。

如今你們都用arch x86_64的內核,內核不會出現這個問題,arch arm和arch arm64也不會有這個問題,只是我機器資源有限只能使用32位的內核,因此碰上了這個問題。x86 32位的內核必須使用FLAT內存管理模型,即CONFIG_FLATMEM_ENABLE=y,才能夠正常使用/proc/kcore。

相關文章
相關標籤/搜索