首先看一下顶层Makefile生成的vmlinux以及arch/arm/boot/compressed/makefile生成的vmlinux的起始地址。
1.1 arch/arm/kernel/vmlinux.lds文件的生成
通过顶层Makefile中的规则生成vmlinux是根据arch/arm/kernel/vmlinux.lds这个脚本链接生成的。arch/arm/kernel/vmlinux.lds是由arch/arm/kernel/vmlinux.lds.S生成的,其生成规则在scripts/Makefile.build的第236行开始定义
quiet_cmd_cpp_lds_S = LDS $@
cmd_cpp_lds_S &#61; $(CPP) $(cpp_flags) -D__ASSEMBLY__ -o $&#64; $<
%.lds: %.lds.S FORCE
$(call if_changed_dep,cpp_lds_S)
1.2顶层vmlinux的起始地址
在arch/arm/kernel/vmlinux.lds.S的开始处有
#ifdef CONFIG_XIP_KERNEL
. &#61; XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
#else
. &#61; PAGE_OFFSET &#43; TEXT_OFFSET; (即0xc0008000)
#endif
我们这里的起始地址就是PAGE_OFFSET &#43; TEXT_OFFSET。
在include/asm-arm/memory.h的49行开始有
#ifndef PAGE_OFFSET
#define PAGE_OFFSET UL(0xc0000000)
#endif
而arch/arm/kernel/vmlinux.lds.S的开头有
#include
asm是一个符号&#xff0c;链接到asm-arm上的
在arch/arm/Makefile第140行&#xff0c;有
TEXT_OFFSET :&#61; $(textofs-y)
第90行有
textofs-y :&#61; 0x00008000
所以TEXT_OFFSET :&#61; 0x00008000
在153行有export TEXT_OFFSET将此变量输出。这样arch/arm/kernel/vmlinux.lds.S也就获得了PAGE_OFFSET &#43; TEXT_OFFSET的值。
1.3SEP4020的虚实地址;
(1)在arch/arm/mach-sep4020/Makefile.boot文件定义了一个压缩内核镜像zImage的起始地址
zreladdr-$(CONFIG_ARCH_4020) :&#61; 0x30008000
这个地址在制作boot目录下面的zImage&#xff0c;uImage时候会用到的&#xff0c;这可以在arch/arm/boot/Makefile中的21行有定义
ZRELADDR :&#61; $(zreladdr-y)
PARAMS_PHYS :&#61; $(params_phys-y)
INITRD_PHYS :&#61; $(initrd_phys-y)
(2)在/include/asm-arm/arch-sep4020/memory.h中定义了一个物理的页偏移地址&#xff0c;即sdram的地址
/*
* Page offset: 3GB
*/
#define PHYS_OFFSET UL(0x30000000)
这个地址在启动代码arch/arm/kernel/head.s中会用到的&#xff1a;
#define KERNEL_RAM_ADDR (PAGE_OFFSET &#43; TEXT_OFFSET) &#xff20;其中TEXT_OFFSET &#xff1d; 0x8000
//swapper_pg_dir是放启动时的临时页表的页表基址(虚地址)
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_ADDR - 0x4000
在这部分要建立页表的时候会用到这个地址的。
1.4压缩的自引导镜像arch/arm/boot/compressed/vmlinux的起始地址
现在看看arch/arm/boot/compressed/makeflie生成的vmlinux。它是根据arch/arm/boot/compressed/vmlinux.lds链接脚本生成的。这个脚本由arch/arm/boot/compressed/vmlinux.lds.in生成&#xff0c;在这个文件的开始处有
. &#61; TEXT_START;
现在看arch/arm/boot/compressed/Makefile&#xff0c;在110行有
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config
&#64;sed "$(SEDFLAGS)" <$ $&#64;
这就是由vmlinux.lds.in生成vmlinux.lds的规则&#xff0c;在它的命令中有个变量SEDFLAGS&#xff0c;在74行定义
SEDFLAGS &#61; s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
这里就把TEXT_START换成了ZTEXTADDR。再往上看从arch/arm/boot/compressed/makeflie的66行起
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR :&#61; $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR :&#61; $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR :&#61; 0
ZBSSADDR :&#61; ALIGN(4)
endif
如果zImage是从ram中启动ZTEXTADDR :&#61; 0&#xff0c;否则从rom或flash启动时ZTEXTADDR :&#61; $(CONFIG_ZBOOT_ROM_TEXT)&#xff0c;这里要在配置时设定CONFIG_ZBOOT_ROM_TEXT的值。
1.5 /arch/arm/kernel/vmlinux.lds.s链接文件的分析
下面先看下这个文件&#xff1a;
#include
#include
#include
#include
OUTPUT_ARCH(arm) /*指定目标板体系结构*/
ENTRY(stext) /*代码段入口*/
jiffies &#61; jiffies_64; /*在/kernel/Timer.c中定义的*/
SECTIONS /*代码段各部分*/
{
. &#61; PAGE_OFFSET &#43; TEXT_OFFSET; /*代码段起始地址&#xff0c;SEP4020 Linux内核是0xC0008000&#xff0c;‘.’表示连接地址*/
.init : { /*内核初始化的代码和数据*/
_stext &#61; .; /*标号_stext表示的就是起始地址0xc0008000*/
_sinittext &#61; .;
*(.init.text)
_einittext &#61; .;
__proc_info_begin &#61; .;
*(.proc.info.init)
__proc_info_end &#61; .;
__arch_info_begin &#61; .;
*(.arch.info.init)
__arch_info_end &#61; .;
__tagtable_begin &#61; .;
*(.taglist.init)
__tagtable_end &#61; .;
. &#61; ALIGN(16);
__setup_start &#61; .;
*(.init.setup)
__setup_end &#61; .;
__early_begin &#61; .;
*(.early_param.init)
__early_end &#61; .;
__initcall_start &#61; .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end &#61; .;
__con_initcall_start &#61; .;
*(.con_initcall.init)
__con_initcall_end &#61; .;
__security_initcall_start &#61; .;
*(.security_initcall.init)
__security_initcall_end &#61; .;
. &#61; ALIGN(32);
__initramfs_start &#61; .;
usr/built-in.o(.init.ramfs)
__initramfs_end &#61; .;
. &#61; ALIGN(64);
__per_cpu_start &#61; .;
*(.data.percpu)
__per_cpu_end &#61; .;
}
/DISCARD/ : { /*内核退出的代码和数据*/
*(.exit.text)
*(.exit.data)
*(.exitcall.exit)
}
.text : { /*真正的代码段部分*/
_text &#61; .; /*代码和只读数据*/
*(.text)
SCHED_TEXT
LOCK_TEXT
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.got) /* Global offset table */
}
RODATA
_etext &#61; .; /*代码段和只读数据结束*/
. &#61; ALIGN(THREAD_SIZE);
__data_loc &#61; .;
.data : AT(__data_loc) { /*数据段起始*/
__data_start &#61; .; /*内存中的地址*/
/*
* first, the init task union, aligned
* to an 8192 byte boundary.
*/
*(.init.task)
. &#61; ALIGN(4096);
__nosave_begin &#61; .;
*(.data.nosave)
. &#61; ALIGN(4096);
__nosave_end &#61; .;
/*
* then the cacheline aligned data
*/
. &#61; ALIGN(32);
*(.data.cacheline_aligned)
/*
/*例外修正表(可能需要在运行时修正)*/
*/
. &#61; ALIGN(32);
__start___ex_table &#61; .;
*(__ex_table)
__stop___ex_table &#61; .;
/*
/*普通的数据段*/
*/
*(.data)
CONSTRUCTORS
_edata &#61; .;
}
.bss : { /*未初始化的全局变量*/
__bss_start &#61; .; /* BSS */
*(.bss)
*(COMMON)
_end &#61; .;
}
/*调试信息和数据段.*/
.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) }
}
/*
* These must never be empty
* If you have to comment these two assert statements out, your
* binutils is too old (for other reasons as well)
*/
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")