/proc/kcore 是受权限和配置限制的虚拟文件,不可直接用 fread 读取;真正的内核转储是 kdump 生成的 vmlinux+vmcore 或 qemu 的 dump,需用 libdw/libelf 解析符号,vmcore 中指针为物理地址,须结合 vmcoreinfo 校准映射。

Linux 下的 /proc/kcore 不是真正的内核转储,别直接用 fread 读它
很多人看到 /proc/kcore 就以为是内核内存镜像,试图用 C++ std::ifstream 或 fopen + fread 直接加载——结果要么失败(Operation not permitted),要么读出全是零。这是因为 /proc/kcore 是一个虚拟文件,内核只在 read() 时按需生成内容,且受 ptrace 权限和 kernel.kptr_restrict 控制。普通用户进程默认无权访问,即使 root 运行,也常因 CONFIG_PROC_KCORE 未启用或 vm.panic_on_oom=2 等配置导致不可读。
真正可分析的内核转储,是 crash 工具(如 kdump)生成的 vmlinux + vmcore 配对文件,或 qemu 的 -dump-guest-core 输出。C++ 程序要处理它们,必须依赖外部解析逻辑,不能靠裸 I/O。
用 libdw + libelf 解析 vmlinux 符号表,否则地址无法映射
vmlinux 是带调试信息的内核镜像(ELF 格式),没有它,你连 sys_call_table 在哪都不知道。C++ 里不能只用 std::ifstream 读二进制头,得用 ELF 解析库定位 .symtab、.strtab 和 .debug_* 段。
- 必须链接
-ldw -lelf,用elf_begin()打开vmlinux,再调dwarf_begin_elf() -
dwarf_getfuncs()可遍历所有函数符号,但注意:内联函数、编译器优化掉的符号不会出现 - 获取
sys_open地址示例:dwarf_offdie(dwarfd, offset, &die)→dwarf_attr(&die, DW_AT_low_pc, &attr)→dwarf_formudata(&attr, &addr) - 若
vmlinux被 strip 过(常见于发行版),.symtab为空,此时必须配合System.map文本文件做地址查表
vmcore 是 ELF core dump,但节区布局和用户态不同,libelf 会误判
vmcore 看似是 ELF 文件(魔数 7f 45 4c 46),但它的 PT_LOAD 段描述的是物理内存页帧,不是虚拟地址;且包含专有节区如 .mem、.note.linux。直接用 elf_getphdrnum() 遍历程序头可能跳过关键内存块。
立即学习“C++免费学习笔记(深入)”;
正确做法是先识别 vmcore 类型:
- 检查
elf_getehdr()后的e_ident[EI_OSABI]:若为ELFOSABI_LINUX且存在NT_PRSTATUSnote,则可能是传统 kdump 格式 - 更可靠的是读取开头 8 字节 magic:
0x45 0x4c 0x46 0x02 0x01 0x01 0x00 0x00(64 位小端)后,跳过 ELF header,解析struct vmcoreinfo_data(位于.note.vmcoreinfo)获取页大小、架构偏移等元信息 - 内存数据实际存储在
PT_LOAD段的p_offset处,但p_vaddr是物理地址,需结合vmcoreinfo中的phys_base做校准
不要手写内存遍历逻辑,用 crash 的 Python API 或 gcore 做预处理
C++ 直接解析 vmcore 容易踩坑:页对齐错误导致读越界、压缩格式(gzip/lzma)未解包、稀疏内存块跳过逻辑写错。生产环境建议绕过裸解析:
- 用
crash -s vmlinux vmcore --minimal -i 'rd /v sys_call_table'抽取关键结构体地址,输出为 JSON,C++ 程序只负责读这个 JSON - 若需完整内存视图,先用
cp /proc/kcore /tmp/kcore.raw(root 权限下)+gcore -o /tmp/kernel.gcore $(pidof systemd)生成标准 core,再用libbfd解析(比libelf更兼容 core 格式) - 对实时性要求高的场景(如 eBPF 辅助分析),改用
libkdumpfile(kdump 官方库),它封装了vmcore的所有变种解析逻辑,C++ 只需调kdump_file_open()+kdump_read_mem()
最常被忽略的一点:vmcore 中的指针全是物理地址,而 vmlinux 符号表给的是编译时的虚拟地址。两者之间差一个 phys_to_virt 映射,这个偏移不在 ELF 里,得从 vmcoreinfo 的 phys_base 和内核启动日志里的 Memory: ...K/...K available 推算——没这步,所有结构体字段访问都是错的。



















