vmlinux.lds.S

vmlinux.lds.S用于对ld的输出进行组版,这个文件的格式在ld.info手册中有详细的说明。vmlinu

x.lds.S的主要目的是对输出文件中段进行排序,并定义相关的符号名,以下是简要注释。

/* ld script to make i386 Linux kernel
 * Written by Martin Mares ;
 */
OUTPUT_FORMAT("elf32-i386", "elf32-i386","elf32-i386")
OUTPUT_ARCH(i386)                /*输出格式*/
ENTRY(_start)                        /*定义_start作为入口点*/
SECTIONS
{
  . = PAGE_OFFSET_RAW + 0x100000; /* 定义当前段的偏移量(.代表当前计数器)*/
  _text =.;                        /*定义符号_text为当前位置  */
  .text :{                /*定义段.text (": {"是段定义符)*/
        *(.text)        /*将所有输入文件中.text段合并到这里*/
        *(.fixup)        /*将所有输入文件中的.fixup段合并到这里*/
        *(.gnu.warning) /* 将所有输入文件中的.gnu.warning段合并到这里*/
        } =0x9090        /* 合并中的空隙用0x9090填充*/
                        /*以下的语法含义可以类推 */
  .text.lock : { *(.text.lock)}        /* out-of-line lock text*/
  .rodata : { *(.rodata) }
  .kstrtab : { *(.kstrtab) }

. =ALIGN(16);                /*Exception table */
  __start___ex_table =.;        /* 定义__start_ex_table符号为当前位置*/
  __ex_table : { *(__ex_table)}
  __stop___ex_table = .;

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

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

.data :{                        /*Data*/
        *(.data)
        CONSTRUCTORS                /*将C++的构造函数指针段合并到这里*/
        }

_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(4096);                /*输出计数器在页边界上对齐 */
  __init_end = .;

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

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

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

/* Stabs debugging sections. */
  .stab 0 : { *(.stab)}        /* 0 是段属性,代表段的起始地址*/
  .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) }
}

以下是用"objdump --headersvmlinux"得到的组版结果:

vmlinux:     file format elf32-i386

Sections:
Idx Name          Size      VMA      LMA       File off  Algn
  0 .text         0009ccb8  c0100000 c0100000  00001000  2**4
                  CONTENTS, ALLOC, LOAD,READONLY, CODE
  1 .text.lock    00000622 c019ccc0  c019ccc0  0009dcc0 2**4
                  CONTENTS, ALLOC, LOAD,READONLY, CODE
  2 .rodata       0000f4ab c019d2e4  c019d2e4  0009e2e4 2**2
                  CONTENTS, ALLOC, LOAD,READONLY, DATA
  3 .kstrtab      00002d9c c01ac78f  c01ac78f  000ad78f 2**0
                  CONTENTS, ALLOC, LOAD,READONLY, DATA
  4 __ex_table    00000ba8 c01af530  c01af530  000b0530 2**2
                  CONTENTS, ALLOC, LOAD,READONLY, DATA
  5 __ksymtab     00001870 c01b00d8  c01b00d8  000b10d8 2**2
                  CONTENTS, ALLOC, LOAD,READONLY, DATA
  6 .data         000133c0  c01b1950 c01b1950  000b2950  2**4
                  CONTENTS, ALLOC, LOAD,DATA
  7 .data.init_task 00002000  c01c6000 c01c6000  000c6000  2**2
                  CONTENTS, ALLOC, LOAD,DATA
  8 .text.init    0000868e c01c8000  c01c8000  000c8000 2**2
                  CONTENTS, ALLOC, LOAD,READONLY, CODE
  9 .data.init    00003220 c01d0690  c01d0690  000d0690 2**2
                  CONTENTS, ALLOC, LOAD,DATA
 10 .data.cacheline_aligned 00001c20  c01d4000 c01d4000  000d4000  2**2
                  CONTENTS, ALLOC, LOAD,DATA
 11 .data.page_aligned 00000800  c01d6000 c01d6000  000d6000  2**2
                  CONTENTS, ALLOC, LOAD,DATA
 12 .bss          0001f324  c01d6800 c01d6800  000d6800  2**4
                  ALLOC
 13 .comment      000011d0 00000000  00000000  000d6800 2**0
                  CONTENTS,READONLY
 14 .note         000011d0  000011d0 000011d0  000d79d0  2**0
                  CONTENTS, READONLY

更多0

 

 

 

vmlinuz自然就是内核了,initrd.img是一个小的映象,包含一个最小的linux系统。通常的步骤是先启动内核,然后内核挂载initrd.img,并执行里面的脚本来进一步挂载各种各样的模块,然后发现真正的root分区,挂载并执行/sbin/init... ...。

initrd.img当然是可选的了,如果没有initrd.img,内核就试图直接挂载root分区。

之所以要有initrd,那是为了启动的时候有更大的灵活性。比如,你把ext3支持编译成模块了。偏偏你的root分区又是ext3的。这下就麻烦了。因为内核需要挂载root分区之后才能加载ext3支持。但是没有ext3支持就没法挂载root分区。initrd就是用来解决这个问题的。

类似的用这个东西还可以做其他的事情,比如从usb盘启动linux也会面临上面类似的问题。用initrd就能搞定了。

甚至,我想在有些嵌入式设备里面都不需要真正的root分区,用initrd就足够搞定一切了。

 

一位网友写得一篇文章,写得很好,加深了对Linux启动的认识,贴在这里:

内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,但是该文件是由vmlinux-armv.lds.in生成的,根据编译选项的不同源文件还可以是vmlinux-armo.lds.in,vmlinux-armv-xip.lds.in。

vmlinux-armv.lds的生成过程在kernel/arch/arm/Makefile中

LDSCRIPT     = arch/arm/vmlinux-armv.lds.in

arch/arm/vmlinux.lds:arch/arm/Makefile $(LDSCRIPT) /

 $(wildcard include/config/cpu/32.h) /

 $(wildcard include/config/cpu/26.h) /

 $(wildcard include/config/arch

        _stext = .;

        __init_begin = .;

            *(.text.init)

        __proc_info_begin = .;

            *(.proc.info)

        __proc_info_end = .;

        __arch_info_begin = .;

            *(.arch.info)

        __arch_info_end = .;

        __tagtable_begin = .;

            *(.taglist)

        __tagtable_end = .;

            *(.data.init)

        . = ALIGN(16);

        __setup_start = .;

            *(.setup.init)

        __setup_end = .;

        __initcall_start = .;

            *(.initcall.init)

        __initcall_end = .;

        . = ALIGN(4096);

        __init_end = .;

    }

   

其中TEXTADDR就是内核启动的虚拟地址,定义在kernel/arch/arm/Makefile中:

ifeq ($(CONFIG_CPU_32),y)

PROCESSOR    = armv

TEXTADDR     = 0xC0008000

LDSCRIPT     = arch/arm/vmlinux-armv.lds.in

endif

需要注意的是这里是虚拟地址而不是物理地址。

一般情况下都在生成vmlinux后,再对内核进行压缩成为zImage,压缩的目录是kernel/arch/arm/boot。

下载到flash中的是压缩后的zImage文件,zImage是由压缩后的vmlinux和解压缩程序组成,如下图所示:

            |-----------------|/    |-----------------|

            |                 | /   |                 |

            |                 |  /  | decompress code |

            |     vmlinux     |   /|-----------------|    zImage

            |                 |   /|                 |

            |                 |     |                 |

            |                 |     |                 |   

            |                 |     |                 |

            |                 |   /|-----------------|

            |                 |   /

            |                 |  /

            |                 | /

            |-----------------|/

           

zImage链接脚本也叫做vmlinux.lds,位于kernel/arch/arm/boot/compressed。

是由同一目录下的vmlinux.lds.in文件生成的,内容如下:

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

 {

   . = LOAD_ADDR;

   _load_addr = .;

 

   . = TEXT_START;

   _text = .;

 

   .text : {

     _start = .;

    

其中LOAD_ADDR就是zImage中解压缩代码的ram偏移地址,TEXT_START是内核ram启动的偏移地址,这个地址是物理地址。

在kernel/arch/arm/boot/Makefile文件中定义了:

ZTEXTADDR   =0

ZRELADDR     = 0xa0008000

ZTEXTADDR就是解压缩代码的ram偏移地址,ZRELADDR是内核ram启动的偏移地址,这里看到指定ZTEXTADDR的地址为0,

明显是不正确的,因为我的平台上的ram起始地址是0xa0000000,在Makefile文件中看到了对该地址设置的几行注释:

# We now have a PICdecompressor implementation.  Decompressors running

# from RAM should notdefine ZTEXTADDR. Decompressors running directly

# from ROM or Flash must defineZTEXTADDR (preferably via the config)

他的意识是如果是在ram中进行解压缩时,不用指定它在ram中的运行地址,如果是在flash中就必须指定他的地址。所以这里将ZTEXTADDR指定为0,也就是没有真正指定地址。

在kernel/arch/arm/boot/compressed/Makefile文件有一行脚本:

SEDFLAGS    =s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;s/BSS_START/$(ZBSSADDR)/

使得TEXT_START =ZTEXTADDR,LOAD_ADDR = ZRELADDR。

这样vmlinux.lds的生成过程如下:

vmlinux.lds:    vmlinux.lds.inMakefile $(TOPDIR)/arch/$(ARCH)/boot/Makefile $(TOPDIR)/.config

 @sed "$(SEDFLAGS)" <vmlinux.lds.in > $@

 

以上就是我对内核启动地址的分析,总结一下内核启动地址的设置:

1、设置kernel/arch/arm/Makefile文件中的

   TEXTADDR     = 0xC0008000

   内核启动的虚拟地址

2、设置kernel/arch/arm/boot/Makefile文件中的

   ZRELADDR     = 0xa0008000

   内核启动的物理地址

   如果需要从flash中启动还需要设置

   ZTEXTADDR地址。

 

源文档 <http://blog.csdn.net/zhoujiaxq/article/details/24347061

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值