The system.map File (ystem.map文件)本文翻译自http://rlworkman.net/system.map/System.map文件(The sys
The system.map File (ystem.map文件)
本文翻译自http://rlworkman.net/system.map/
System.map文件(The system.map File)关于System.map文件的信息比较缺乏。该文件并不神秘,而且在linux发展过程中并不是十分重要。但是由于缺乏相关的文档资料让他看起来很隐蔽。system.map文件就像耳垂,每个人都有,但是没有人知道为什么会有。本文汇集了几个网页来阐述为什么会有system.map文件。
值得注意的是,本文并不能保证百分百正确。比如说,系统游客能不支持/proc文件系统,但是大多数系统支持。本文针对主流并且相对典型的系统。
有些人对“Linux Device Drivers”这本书不满,而我大部分的内核编程的知识都是在这本书上学到的。
什么是符号?(What Are Symbols?)在计算机世界中,符号指的是程序建立的块:它有可能是变量名也有可能是函数名。正如你写的程序一样,内核也是程序,也是有符号的。不同的是,内核是非常复杂的一部分代码,以及很多全局符号。
什么是内核符号表?(What Is The Kernel Symbol Table?)
内核并不是像使用BytesRead()函数那样来使用符号。当通过变量或者函数的地址知道变量或者函数得名字的时候会比较令人喜悦,比如c0343f20。从另一各方面说,人们平时不习惯于使用(变量或者函数)地址,比如c0343f20。我们更习惯于使用BytesRead()这样的函数名。通常情况下,使用BytesRead()并没有什么问题。因为内核主要是用C语言编写,所以,编译器/连接器允许我们使用在写代码的时候使用变量名或者函数名,并且在内核运行的时候允许使用地址。这是个令人喜悦的消息。
但是也有写情况下,我们需要知道函数或者变量的地址。这些工作都由符号表完成,这点与GDB非常相似,GDB可以告诉你某个地址的函数名(或者某个函数的地址)。符号表列出了所有的符号以及他们的地址。例表如下:
c03441a0 B dmi_broken c03441a4 B is_sony_vaio_laptop c03441c0 b dmi_ident c0344200 b pci_bios_present c0344204 b pirq_table c0344208 b pirq_router c034420c b pirq_router_dev c0344220 b ascii_buffer c0344224 b ascii_buf_bytes
从上表中可以看出名为dmi_broken的变量的内核地址为c03441a0。
什么是System.map文件?(What Is The System.map File?)有两个文件被用于内核符号表:/proc/kallsymsSystem.map
这里你知道什么是System.map文件。
每次编译新内核的时候,与之关联的符号名对应的地址就会发生变化。
/proc/kallsyms是个“过程文件”,该文件在内核启动的时候创建。事实上,该文件并非真实的磁盘文件;它拥有磁盘文件的表象,但实际上代表了内核数据。如果你不相信,尝试查看/proc/kallsyms文件的大小。因此,在内核运行过程中,该文件会被不断的修改。
但是,systemp.map是文件系统中真实存在的文件。当你编译新内核的时候,旧的system.map中的信息被认为是错误的符号信息。每个新内核编译的时候都会产生新的system.map文件,你需要用新文件替换旧文件。
什么是内核错误?(What Is An Oops?)你自己写的程序中最常见的bug是什么?段错误。
linux内核中最常见的bug是什么呢?段错误。除此之外,段错误的概念可以更加复杂,你可以想想到,更加严重。当内核对错误的指针进行解引用操作的时候,我们称之为Oops,而非段错误。Oops指的是内核bug,并且应该被报告和修复。
值得注意的是,Oops错误与段错误并不相同。你的程序通常情况下不能从段错误中恢复。当Oops错误发生的时候,内核不必要在一个不稳定状态。Linux 内核具有非常好的鲁棒性,内核Oops只会杀死当前的进程,并且使内核的其他部分处于一个相对稳定的状态。
oops不是内核错误。当内核错误发生的时候,内核不能够继续执行;系统会中止,并且必须重启。如果系统的关键部分被毁掉,oops可能会导致内核错误。比如,oops在发生在设备驱动中,永远不会导致内核错误。
当oops发生的时候,系统会输出调试的相关信息。类似于CPU所有寄存器的内容,以及页描述表地址。特别的是,EIP(指令指针)的内容会被输出,例如:
EIP: 0010:[<00000000>] Call Trace: [<c010b860>]
oops跟system.map的关系(What Does An Oops Have To Do With System.map?)给出的EIP信息和调用跟踪信息并不是十分足够。由于内核符号在内核启动之前没有固定地址,所以c010b860可以指向任何内核符号。如果你之报告一个地址给开发者,内核开发者无法根据这些细微的线索来查找bug。他们需要符号名称来找bug。
为了帮助理解神秘的oops输出,做了一个例子程序klogd,内核登录例子,用于展示符号地址转换。当opps发生的时候,klogd拦截oops报告,把地址转换为符号名(比如, 将c010b860 转换为 BytesRead()),并且通过system logger(通常称为syslogd)来记录触发oops的时间。
为了展示内核符号地址解决方案,klogd使用system.map。
这里,你知道oops和system.map的关系了吧。
Fine print:klogd输出两种类型的地址解决方案。
1.通过system.map文件静态翻译。
2.通过可载入的模块动态翻译。这样的翻译不需要使用system.map文件,虽然本文不涉及这个,但是我简单说几句:
Klogd动态翻译(Klogd Dynamic Translation)假设你再度的内核导致了一个oops。klogd捕获了产生的oops消息。它发现oops发生地址为d00cf810。由于这个地址属于动态可载入模块,所以它在system.map文件中不存在记录。klogd在system.map文件中搜索不到它,从而确定一定是可载入模块产生了这个oops。klogd在可载入模块输出中查找内核符号。即使模块没有输出任何符号,最起码klogd会知道哪个模块产生了这个oops,这总比对oops一无所知要好得多。
system.map文件应该在哪儿?(Where should system.map Be Located?)
System.map文件的位置应该是固定的,方便软件在使用它的时候找到他。直到某些标准(或者明确的权威)规定它的位置和名字的时候才知道答案。记住这点,下面让我们来看一下某些软件,看看他们希望system.map文件在哪里被找到。
Klogd
如果klogd在执行的时候不给出-k选项的system.map的地址,klogd默认会在下面地址搜索它(来自于源代码文件ksym.c): static char *system_maps[] = { "/boot/System.map", "/System.map", #if defined(TEST) "./System.map", #endif (char *) 0 };
klogd在这些目录里面寻找"system.map"文件以及"system.map-release"文件,"-release"表示内核版本号。这个一个明智的做法:如果klogd找到找到system.map文件,并且发现该文件的内核版本号预先在运行的内核版本号不同,他会继续寻找下去。
尽管klogd主页以及源代码评论认为/usr/src/linux在搜索目录中,我无法找到关于这个的参考。我已经将这个问题报告给了Debian BTS 以及Dr. G.W. Wettstein(ksym.c的作者)。
设备驱动(Device Drive)system.map不仅仅用于调试内核oopes。部分驱动程序需要system.map用来解决符号,因为他们链接到内核头部而非glibc。他们在没有system.map的情况下针对当前正在运行的内核不能正确执行。这跟由于内核版本不匹配无法加载模块不是一回事,无法加载模块与内核版本有关,与内核符号表无关。ps:
ps使用与klogd不同的搜索数组
*sysmap_paths[] = { "/boot/System.map-%s", "/boot/System.map", "/lib/modules/%s/System.map", "/usr/src/linux/System.map", "/System.map", NULL };
此处%会被当前运行的内核版本号代替。
还有哪些地方会用到system.map(What else uses (or doesn't use) the System.map)在某一时刻(2003年五月),我认为lsof以及dosemu回用到system.map,但是通过查看源代码(2007年五月),他们并未使用到system.map。
如果没有一个健康的system.map文件会发生什么(What Happens If I Don't Have A Healthy System.map?)假设我同一台机器上有多个不同的内核。针对不同内核需要不同的system.map文件。如果运行一个没喝得时候没有或者有一个错误的system.map文件,你可能周期性的看到下面的警告:
System.map does not match actual kernel(system.map与真正的内核不匹配)
每次使用ps命令,或者klogd命令的时候,由于内核oops,输出会不可靠。