[置顶] 经过长达六年的研发,Linux Lab 终于迎来了 v1.0 正式版,Linux 内核与嵌入式 Linux 开发从未像今天这般简单。Linux Lab 发布 v1.0 正式版,Linux 内核开发从未像今天这般简单
探索从 vmlinux 中抓取 Linux 内核 config 文件
By Falcon of TinyLab.org Jul 18, 2019
背景简介
最近两周在忙活 Linux Lab V0.2 RC1,其中一个很重要的目标是添加国产龙芯处理器支持。
在添加龙芯 ls2k 平台的过程中,来自龙芯的张老师已经准备了 vmlinux 和 dtb,还需要添加配置文件和源代码,但源码中默认的配置编译完无法启动,所以需要找一个可复用的内核配置文件。
在张老师准备的内核 vmlinux 中,确实有一个 /proc/config.gz
,说明内核配置文件已经编译到内核了,但是由于内核没有配置 nfs,尝试了几次没 dump 出来。
当然,其实也可以用 zcat /proc/config.gz
打印到控制台,然后再复制出来,这个时候要把控制台的 scrollback lines 设置大一些,但是没那么方便。
极速体验
这里讨论另外一个方法,这是张老师分享的一个小技巧,那就是直接用 Linux 内核源码下的小工具:script/extract-ikconfig
。
$ cd /path/to/linux-kernel
$ scripts/extract-ikconfig /path/to/vmlinux
执行完的结果跟 zcat 一致,需要保存到文件,可以这样:
$ scripts/extract-ikconfig /path/to/vmlinux > kconfig
需要注意的是,这个前提是配置内核时要开启 CONFIG_IKCONFIG
选项。而如果要拿到 /proc/config.gz
,还得打开 CONFIG_IKCONFIG_PROC
。
原理分析
大概的原理我们来剖析一下。
Makefile
初始化 KCONFIG_CONFIG
:
KCONFIG_CONFIG ?= .config
kernel/Makefile
把 .config
用 gzip 压缩了一份,放到了 kernel/config_data.gz
:
$(obj)/configs.o: $(obj)/config_data.gz
targets += config_data.gz
$(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE
$(call if_changed,gzip)
kernel/configs.c
把 kernel/config_data.gz
放到 .rodata
section,并在前后加了字符串标记:IKCFG_ST
和 IKCFG_ED
:
/*
* "IKCFG_ST" and "IKCFG_ED" are used to extract the config data from
* a binary kernel image or a module. See scripts/extract-ikconfig.
*/
asm (
" .pushsection .rodata, \"a\" \n"
" .ascii \"IKCFG_ST\" \n"
" .global kernel_config_data \n"
"kernel_config_data: \n"
" .incbin \"kernel/config_data.gz\" \n"
" .global kernel_config_data_end \n"
"kernel_config_data_end: \n"
" .ascii \"IKCFG_ED\" \n"
" .popsection \n"
);
scripts/extract-ikconfig
通过 grep -abo
去找到 kconfig data 的位置。-abo
的意思是:-a
把二进制文件当 text 处理,-b
打印字节偏移,-o
只打印要匹配的字符串:
dump_config()
{
if pos=`tr "$cf1\n$cf2" "\n$cf2=" < "$1" | grep -abo "^$cf2"`
then
pos=${pos%%:*}
tail -c+$(($pos+8)) "$1" | zcat > $tmp1 2> /dev/null
if [ $? != 1 ]
then # exit status must be 0 or 2 (trailing garbage warning)
cat $tmp1
exit 0
fi
fi
}
这个脚本写得有点晦涩,大体意思是先找到 “IKCFG_ST”,算出 kconfig data 位置,再用 tail 取出来。
换个思路
我们自己换个更清晰的思路。
先看看 vmlinux 和 kernel/config_data.gz
的布局:
"IKCFG_ST ..... IKCFG_ED" --> vmlinux
^ kernel/config_data.gz ^ --> kernel/config_data.gz
首先,找出 IKCFG_ST
和 IKCFG_ED
的位置。然后换算出 kernel/config_data.gz
的前后位置:
$ egrep -abo "IKCFG_ST|IKCFG_ED" boards/loongson/ls2k/bsp/kernel/v3.10/vmlinux
14508864:IKCFG_ST
14529536:IKCFG_ED
kernel/config_data.gz
的起始地址需要加上 “IKCFG_ST” 的长度,即 +8
:$((14508864+8))
,而结束地址刚好是 “IKCFG_ED” 的地址 -1
:$((14529536-1))
,总的 size 是:
$ echo $(((14529536-1) - (14508864+8) + 1))
20664
这样,我们就可以用 dd
命令截取出来:
$ dd if=boards/loongson/ls2k/bsp/kernel/v3.10/vmlinux bs=1 skip=$((14508864+8)) count=20664 of=kconfig.gz
$ file kconfig.gz
kconfig.gz: gzip compressed data, max compression, from Unix
$ zcat kconfig.gz
完美!逻辑上更清晰,基于这个逻辑改写了一个自己的 extract-ikconfig
,见 Linux Lab 下的 tools/kernel/extract-ikconfig。
小结
小技巧,大道理!
上述探索涉及到内联汇编,涉及到如何把一个文件嵌入到内核执行文件中,涉及到如何解析二进制文件并截取想要的内容。
回到这个技巧本身,也不失为 Debugging 的一个必备技能,内核配置文件对于复现问题,找到问题现场必不可少!
再来总结一下:
- 编译内核时,打开
CONFIG_IKCONFIG
和CONFIG_IKCONFIG_PROC
。 - 从 Runtime 内核中抓取:
zcat /proc/config.gz
,如果找不到了 vmlinux,这个不失为一个好方法。 - 从静态内核 vmlinux 中抓取:
scripts/extract-ikconfig /path/to/vmlinux
。
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 ¥9.68元 | |
![]() | ![]() 请作者喝杯咖啡吧 | ![]() |
Read Album:
- LSM Oops 内存错误根因分析与解决
- Linux 下的 Sync 卡死问题分析报告
- 使用 mtrace 分析 “内存泄漏”
- 七张图看懂 Linux profiling 机制
- bugfix: 使用 git bisect 自动定位 uboot 启动失败问题