Linux内核如何生成system.map

Linux 专栏收录该内容
28 篇文章 1 订阅

  编译Linux内核时,会生成System.map文件,它记录了所有代码的运行地址。本文主要说明内核如何生成这个System.map文件。
  顺便扩展下思路,在编写应用程序时,如何参考内核这一机制,生成符号文件。

  System.map文件生成的脚本路径位于内核目录scripts/mksysmap,mksysmap文件内容也非常简单,话不多说上代码:

#!/bin/sh -x
# Based on the vmlinux file create the System.map file
# System.map is used by module-init tools and some debugging
# tools to retrieve the actual addresses of symbols in the kernel.
#
# Usage
# mksysmap vmlinux System.map


#####
# Generate System.map (actual filename passed as second argument)

# $NM produces the following output:
# f0081e80 T alloc_vfsmnt

#   The second row specify the type of the symbol:
#   A = Absolute
#   B = Uninitialised data (.bss)
#   C = Common symbol
#   D = Initialised data
#   G = Initialised data for small objects
#   I = Indirect reference to another symbol
#   N = Debugging symbol
#   R = Read only
#   S = Uninitialised data for small objects
#   T = Text code symbol
#   U = Undefined symbol
#   V = Weak symbol
#   W = Weak symbol
#   Corresponding small letters are local symbols

# For System.map filter away:
#   a - local absolute symbols
#   U - undefined global symbols
#   N - debugging symbols
#   w - local weak symbols

# readprofile starts reading symbols when _stext is found, and
# continue until it finds a symbol which is not either of 'T', 't',
# 'W' or 'w'. __crc_ are 'A' and placed in the middle
# so we just ignore them to let readprofile continue to work.
# (At least sparc64 has __crc_ in the middle).

$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( .L\)' > $2

  可以看到,真正有用的代码只有最后一行。$NM其实就是nm命令。grep -v意思是设置过滤条件。在内核下执行nm -n vmlinux | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( .L\)' > System.map1,可以看到生成的System.map1和编译时生成的System.map一致。

  下面自己写一个应用程序,依样画葫芦,也可以通过这个方式生成符号文件。
程序代码:

#include <stdio.h>

int main()
{
	printf("hello world\n");
	return 0;
}

  使用交叉编译工具链编译:arm-linux-gnueabi-gcc -o hello_world hello_world.c
  编译后使用命令:nm -n hello_world | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( .L\)' > hello_world.map或取出内核的mksysmap文件,放到当前目录,将$NM改为nm,执行命令./mksysmap hello_world hello_world.map
下面来看hello_world.map文件内容:

00010298 T _init
000102e9 T _start
00010318 t call_weak_fn
0001033d t deregister_tm_clones
00010361 t register_tm_clones
0001038d t __do_global_dtors_aux
000103a5 t frame_dummy
000103c9 T main
000103e1 T __libc_csu_init
00010421 T __libc_csu_fini
00010424 T _fini
0001042c R _IO_stdin_used
00010444 r __FRAME_END__
00020448 t __frame_dummy_init_array_entry
00020448 t __init_array_start
0002044c t __do_global_dtors_aux_fini_array_entry
0002044c t __init_array_end
00020450 d __JCR_END__
00020450 d __JCR_LIST__
00020454 d _DYNAMIC
0002053c d _GLOBAL_OFFSET_TABLE_
0002055c D __data_start
0002055c W data_start
00020560 D __dso_handle
00020564 B __bss_start
00020564 B __bss_start__
00020564 b completed.8984
00020564 D _edata
00020564 D __TMC_END__
00020568 B __bss_end__
00020568 B _bss_end__
00020568 B _end
00020568 B __end__

  可以看到可执行文件中所有代码的运行地址。
  注意:可执行文件不能使用交叉编译工具链strip,strip工具会删除可执行文件中的符号,也就无法通过nm获取我们要的符号表了。
  嵌入式Linux内核的做法是,先通过LD链接的Vmlinux生成System.map,再讲Vmlinux去符号、压缩,增加自解压头,生成最终嵌入式设备使用的zImage or uImage。
  同理,编写嵌入式应用程序时,也可以通过先链接生成带符号的可执行文件,通过上文描述的操作,生成符号文件,在通过交叉编译工具strip去除可执行文件中的调试符号。这样既减小了可执行程序的大小,又可以回溯到代码、函数的运行地址。

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值