首页 技术 正文
技术 2022年11月11日
0 收藏 755 点赞 4,407 浏览 4238 个字

为分析内核,在有限的机器上用虚拟机装了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,映射相关的接口。

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 = ;

这里主要有两类地址,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结构进行管理。

// 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 - * PAGE_SIZE)
#else
# define VMALLOC_END (FIXADDR_START - * 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。

先来看kcore是如何将kcore文件的线性地址映射到内核地址的,下面的相关函数的反汇编代码:

/proc/kcore失效,调试其文件系统相关模块,使重新正常工作

这里要注意的是,代码是被-O2优化过的,反汇编并不能与代码很好地对应,开头两行可能是承着某处的跳转的。从行文来看%esi优化为变量elf_buflen。

将图的下面4行汇编逆向为 0xc0000000 – elf_buflen + *fpos。 也就是 offset + 3G – sizeof(ELF core header)。

再来看kcore一共有几个映射区间,只要walk through一下kclist_head队列就可以知道。自己写一下调试脚本就可以得到。

/proc/kcore失效,调试其文件系统相关模块,使重新正常工作

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。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,983
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,500
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,344
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,127
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,761
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,838