系统启动(9)构建系统之三次连接生成vmlinux

2929阅读 0评论2012-07-27 qtdszws
分类:LINUX

我们已经知道,为了生成vmlinux,进行了三次连接。这三次连接的目的是为了生成kallsyms符号信息。

2.  ld -m elf_i386 --emit-relocs  -o .tmp_vmlinux1 -T arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.o arch/x86/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/x86/built-in.o kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o crypto/built-in.o  block/built-in.o  lib/lib.a  arch/x86/lib/lib.a  lib/built-in.o arch/x86/lib/built-in.o  drivers/built-in.o  sound/built-in.o  firmware/built-in.o arch/x86/pci/built-in.o  arch/x86/power/built-in.o  arch/x86/video/built-in.o  net/built-in.o --end-group

第一次连接,按照连接脚本vmlinux.lds的指示,生成.tmp_vmlinux1.

在kernel/kallsyms.c中,有如下定义
/*
 * These will be re-linked against their real values
 * during the second link stage.
这些变量将被重连接到对应的真实值在第二连接阶段
 */
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
extern const u8 kallsyms_names[] __attribute__((weak));

/*
 * Tell the compiler that the count isn't in the small data section if the arch
 * has one (eg: FRV).
 */
extern const unsigned long kallsyms_num_syms __attribute__((weak, section(".rodata")));

extern const u8 kallsyms_token_table[] __attribute__((weak));
extern const u16 kallsyms_token_index[] __attribute__((weak));

extern const unsigned long kallsyms_markers[] __attribute__((weak));

所有的符号都是弱符号,第一次连接时,还没有对应的真实值,这些符号都是未定义。未定义的弱符号可以连接成功,符号值是0.
[zws@localhost linux-3.0]$ readelf -s .tmp_vmlinux1|grep UND
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
 49798: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_token_table
 50146: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_token_index
 51147: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_num_syms
 51253: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_addresses
 52976: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_names
 59372: 00000000     0 NOTYPE  WEAK   DEFAULT  UND kallsyms_markers

3.  nm -n .tmp_vmlinux1 | scripts/kallsyms  > .tmp_kallsyms1.S

根据tmp_vmlinux1的符号表生成tmp_kallsyms1.S汇编程序
nm -n按照地址大小排序

[zws@localhost linux-3.0]$ nm -n .tmp_vmlinux1|head -n 100
         w kallsyms_addresses
         w kallsyms_markers
         w kallsyms_names
         w kallsyms_num_syms
         w kallsyms_token_index
         w kallsyms_token_table
00000000 A VDSO32_PRELINK
00000040 A VDSO32_vsyscall_eh_frame_size
000001d3 A kexec_control_code_size
00000400 A VDSO32_sigreturn
0000040c A VDSO32_rt_sigreturn
00000414 A VDSO32_vsyscall
00000424 A VDSO32_SYSENTER_RETURN
0000056c a syscall_table_size
00000c00 a page_pde_offset
00040000 a KERNEL_PAGES
00040000 a LOWMEM_PAGES
00100000 a INIT_MAP_SIZE
00100000 a MAPPING_BEYOND_END
01000000 A phys_startup_32
c1000000 T _text
c1000000 T startup_32
c1001000 T wakeup_pmode_return
c100104c t bogus_magic
c100104e t save_registers
c100109d t restore_registers
c10010c0 T do_suspend_lowlevel
c10010d6 t ret_point
c10010e8 T _stext
c10010f0 T do_one_initcall
c1001260 t run_init_process
c1001280 t init_post
c1001330 T name_to_dev_t
c10015d0 t match_dev_by_uuid
c1001610 T thread_saved_pc
c1001620 T get_wchan
c10016c0 T prepare_to_copy
c1001770 T release_thread
c1001790 T copy_thread
c10018e0 T start_thread
c1001920 T __show_regs
c1001ab0 T cpu_idle
c1001b30 T __switch_to
c1001d00 t setup_sigcontext
c1001dd0 t align_sigframe
c1001de0 T signal_fault

这个地址表经过scripts/kallsyms过滤后生成S文件,scripts/kallsyms的过滤规则主要是只保留_stext和_etext之间,还有_sinittext和_einittext之间的符号。这之间的符号分别是常驻内核代码和内核初始化代码。

生成的S文件内容是:
#include
#if BITS_PER_LONG == 64
#define PTR .quad
#define ALGN .align 8
#else
#define PTR .long
#define ALGN .align 4
#endif
        .section .rodata, "a"
.globl kallsyms_addresses
        ALGN
kallsyms_addresses:
        PTR     _text + 0x10e8
        PTR     _text + 0x10f0
        PTR     _text + 0x1260
        PTR     _text + 0x1280
        PTR     _text + 0x1330
        PTR     _text + 0x15d0
        PTR     _text + 0x1610
        PTR     _text + 0x1620
        PTR     _text + 0x16c0
        PTR     _text + 0x1770
大量的PTR
.globl kallsyms_num_syms
        ALGN
kallsyms_num_syms:
        PTR     30706

.globl kallsyms_names
        ALGN
kallsyms_names:
        .byte 0x05, 0x54, 0xfb, 0xf2, 0x78, 0x74
        .byte 0x09, 0x54, 0x16, 0x5f, 0xeb, 0xfe, 0x60, 0x63, 0xe7, 0x6c
        .byte 0x08, 0xf3, 0xda, 0xcd, 0xbc, 0x70, 0xa8, 0xbf, 0x73
        .byte 0x05, 0x7c, 0xbc, 0x70, 0x1b, 0x74
大量的.byte
.globl kallsyms_markers
        ALGN
kallsyms_markers:
        PTR     0
        PTR     2799
        PTR     5728
        PTR     8979
        PTR     11927
        PTR     15042
大量的PTR
.globl kallsyms_token_table
        ALGN
kallsyms_token_table:
        .asciz  "eee80211"
        .asciz  "eee"
        .asciz  "_co"
        .asciz  "tre"
        .asciz  "_de"
        .asciz  "up_"
大量的.asciz
.globl kallsyms_token_index
        ALGN
kallsyms_token_index:
        .short  0
        .short  9
        .short  13
        .short  17
        .short  21
大量的.short

关于kallsyms的更多内容,清参考以前写的一篇文章
注意,所有的数据都放在了.rodata节中

4.  gcc -Wp,-MD,./..tmp_kallsyms1.o.d -D__ASSEMBLY__ -m32 -DCONFIG_AS_CFI=1        -nostdinc -isystem/usr/lib/gcc/i386-redhat-linux/3.4.3/include -I/root/linux-3.0/arch/x86/include-Iarch/x86/include/generated -Iinclude  -include include/generated/autoconf.h -D__KERNEL__    -c -o.tmp_kallsyms1.o .tmp_kallsyms1.S
将.tmp_kallsyms1.S编译成目标文件


5.  ld -m elf_i386 --emit-relocs  -o .tmp_vmlinux2 -T arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.oarch/x86/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/x86/built-in.o kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o crypto/built-in.o  block/built-in.o  lib/lib.a  arch/x86/lib/lib.a  lib/built-in.o arch/x86/lib/built-in.o  drivers/built-in.o  sound/built-in.o  firmware/built-in.o arch/x86/pci/built-in.o  arch/x86/power/built-in.o  arch/x86/video/built-in.o  net/built-in.o--end-group .tmp_kallsyms1.o
第二次连接,加入文档.tmp_kallsyms1.o,.tmp_kallsyms1.o中的符号都是全局的,且已定义。
[zws@localhost linux-3.0]$ readelf -s .tmp_kallsyms1.o|grep kall
     1: 00000000     0 FILE    LOCA哦L  DEFAULT  ABS .tmp_kallsyms1.S
     6: 00000000     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_addresses
     8: 0001dfc8     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_num_syms
     9: 0001dfcc     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_names
    10: 00074974     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_markers
    11: 00074b54     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_token_table
    12: 00074e94     0 NOTYPE  GLOBAL DEFAULT    4 kallsyms_token_index

这样第二次连接时,将使用这些全局符号替换前面的弱符号,同时数据被存入.rodata节。由于kallsyms中包含初始化代码符号,而初始化代码是在.rodata节后,这样连接成功后,初始化代码的地址向下挪动了,和.tmp_vmlinux1中的不一致了。这样kallsyms中的初始化代码符号地址就不正确了,还需要再进行一次连接,才能解决这个问题。

6.  nm -n .tmp_vmlinux2 | scripts/kallsyms  > .tmp_kallsyms2.S

7.  gcc -Wp,-MD,./..tmp_kallsyms2.o.d -D__ASSEMBLY__ -m32 -DCONFIG_AS_CFI=1        -nostdinc -isystem/usr/lib/gcc/i386-redhat-linux/3.4.3/include -I/root/linux-3.0/arch/x86/include-Iarch/x86/include/generated -Iinclude  -include include/generated/autoconf.h -D__KERNEL__    -c -o.tmp_kallsyms2.o .tmp_kallsyms2.S

8.  ld -m elf_i386 --emit-relocs  -o vmlinux -T arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_32.oarch/x86/kernel/head32.o arch/x86/kernel/head.o arch/x86/kernel/init_task.o  init/built-in.o--start-group  usr/built-in.o  arch/x86/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o  lib/lib.a arch/x86/lib/lib.a  lib/built-in.o  arch/x86/lib/built-in.o  drivers/built-in.o  sound/built-in.o firmware/built-in.o  arch/x86/pci/built-in.o  arch/x86/power/built-in.o  arch/x86/video/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o

由于第二次连接未引入新的代码段,因此第三次生成的kallsyms中的符号数未变,只是部分符号的值需要更正,因此生成的目标文件.tmp_kallsyms2.o大小将不会改变,连接进vmlinux时不会再次移动init代码及符号地址,这样,kallsyms中的符号值都得到纠正。

为了保证这一点,接下来make会执行如下的命令
echo '  /bin/sh /root/linux-3.0/scripts/mksysmap  System.map' && /bin/sh/root/linux-3.0/scripts/mksysmap vmlinux System.map; if [ $? -ne 0 ]; then rm -f vmlinux;/bin/false; fi;
  /bin/sh /root/linux-3.0/scripts/mksysmap  System.map
echo '  /bin/sh /root/linux-3.0/scripts/mksysmap  .tmp_System.map' && /bin/sh/root/linux-3.0/scripts/mksysmap .tmp_vmlinux2 .tmp_System.map
  /bin/sh /root/linux-3.0/scripts/mksysmap  .tmp_System.map
cmp -s System.map .tmp_System.map || (echo Inconsistent kallsyms data; echo This is a bug - pleasereport about it; echo Try "make KALLSYMS_EXTRA_PASS=1" as a workaround; rm .tmp_kallsyms* ;/bin/false )

通过比较System.map和.tmp_System.map,确保kallsysms数据正确。

自此vmlinux生成成功。
上一篇:系统启动(7)main.c
下一篇:系统启动(11)内核解压