文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Linux内核链接器学习

Linux内核链接器学习

时间:2007-05-05  来源:andersma

  通过对Linux内核的不断学习, 越来越发现其复杂性,需要开发人员掌握N多知识,操作系统的知识我想是最基本的了,其次包括汇编,C语言,Make file,bash script等等知识,最近又发现了,在编译连接Linux内核的时候,还涉及到了连接器(LD)的一些知识。   通常在编译C文件的时候,编译器会把文件转换成汇编语言,默认目标文件是包含text,data,bss三个段,然后链接器会把所有的目标程序连接成一个文件,链接过程包括多个文件不同段的整合,也就是不同文件的text段整合在一个text段里面,不同文件的data段整合在一个data段里面....   在Linux内核编译连接后,会生成很多段(不止上面默认的三个)。那个如何定义新段呢?  

  [init.h]
  #define __init __attribute__ ((__section__ (".text.init")))
  [8139cp.c]
  static int __init cp_init (void)

    上面的cp_init的代码就被放置在一个新段".text.init"中,__attribute__ ((__section__ (".text.init")))就是通知编译器在编译时,把此函数体代码放在".text.init"中。其实Linux module方式加载驱动的init代码也都位于此段,把初始化代码都放在此段的原因主要是节约内存,当初始化这些模块后,这个".text.init"区域会被内核收回,linux内核程序员的确都珍惜内存。   如果单单只在内核代码中做上面的事情,还是不够的,上面只是解决了文件编译分块的问题,真正链接的问题还没有解决,那么如何把所有目标文件同样的段集合在一起呢?段位置如何确定呢?这就是链接器的事情了!  

[Linux内核的Makefile]

vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ --start-group \ $(CORE_FILES) \ $(DRIVERS) \ $(NETWORKS) \ $(LIBS) \ --end-group \ -o vmlinux

LINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS)

vmlinux.lds

/* ld script to make i386 Linux kernel
 * Written by Martin Mares <[email protected]>;
 */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
  . = 0xC0000000 + 0x100000;
  _text = .;   /* Text and read-only data */
  .text : {
 *(.text)
 *(.fixup)
 *(.gnu.warning)
 } = 0x9090

  _etext = .;   /* End of text section */

  .rodata : { *(.rodata) *(.rodata.*) }
  .kstrtab : { *(.kstrtab) }

  . = ALIGN(16);  /* Exception table */
  __start___ex_table = .;
  __ex_table : { *(__ex_table) }
  __stop___ex_table = .;

  __start___ksymtab = .; /* Kernel symbol table */
  __ksymtab : { *(__ksymtab) }
  __stop___ksymtab = .;

  .data : {   /* Data */
 *(.data)
 CONSTRUCTORS
 }

  _edata = .;   /* End of data section */

  . = ALIGN(8192);  /* init_task */
  .data.init_task : { *(.data.init_task) }

  . = ALIGN(4096);  /* Init code and data */
  __init_begin = .;
  .text.init : { *(.text.init) }
  .data.init : { *(.data.init) }
  . = ALIGN(16);
  __setup_start = .;
  .setup.init : { *(.setup.init) }
  __setup_end = .;
  __initcall_start = .;
  .initcall.init : { *(.initcall.init) }
  __initcall_end = .;
  . = ALIGN(4096);
  __init_end = .;

  . = ALIGN(4096);
  .data.page_aligned : { *(.data.idt) }

  . = ALIGN(32);
  .data.cacheline_aligned : { *(.data.cacheline_aligned) }

  __bss_start = .;  /* BSS */
  .bss : {
 *(.bss)
 }
  _end = . ;

  /* Sections to be discarded */
  /DISCARD/ : {
 *(.text.exit)
 *(.data.exit)
 *(.exitcall.exit)
 }

  /* Stabs debugging sections.  */
  .stab 0 : { *(.stab) }
  .stabstr 0 : { *(.stabstr) }
  .stab.excl 0 : { *(.stab.excl) }
  .stab.exclstr 0 : { *(.stab.exclstr) }
  .stab.index 0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment 0 : { *(.comment) }
}
上面是Linux ld(链接器)所使用的脚本,也就是在链接linux内核时所参照的文件,ENTRY界定了程序的入口点,这里面时__start,也就是bootsect.S中的其实地址,执行copy内核到指定内存功能,然后用SECTION定义了生成内核文件的分块结构,__setup_start = .;这句话很有意思,在linux内核里面定义了__setup_start这个变量,但就是没有赋值过,嘿嘿,其实赋值在这里呢,"."代表输出文件(内核image)当前链接位置,之前我还疑惑,linux为何有时候定一变量,却没看到初始化,果不其然,原来在这里。通过链接文件的配合,一个符合开发者所规划的linux内核image就产生了。

 

 

相关阅读 更多 +
排行榜 更多 +
螺旋圆舞曲2

螺旋圆舞曲2

角色扮演 下载
追漫大师兄

追漫大师兄

浏览阅读 下载
手画攀爬者

手画攀爬者

休闲益智 下载