linux内存管理 (三) 7 内核启动从start到start_kernel过程中的相关地址在内存中的分布

内核在nand上的分布
烧写的是zImage, 烧写到了 nand 的 100000 地址处
u-boot 传参,并启动u-boot
setenv  bootcmd   nand read C0008000 100000 500000 ; bootm 
 从 nand 地址 100000,500000  大小数据,到 C0008000 ,并从 bootm 启动

此时和 我们说的 uImage 不同,按道理 官方u-boot 只能引导 uImage 这一种内核镜像

除非他修改了u-boot,像链接那样https://blog.csdn.net/ce123_zhouwei/article/details/7336810

果然修改了 u-boot
common/cmd_bootm.c do_bootm 函数
  #ifdef CONFIG_ZIMAGE_BOOT                                                            
  #define LINUX_ZIMAGE_MAGIC  0x016f2818                                               
      if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {                              
          printf("Boot with zImage\n");                                                
          addr = virt_to_phys(addr);  // #define virt_to_phys(x) (x)
          							  // addr = C0008000                                                
          hdr->ih_os = IH_OS_LINUX;                                                    
          hdr->ih_ep = ntohl(addr);   //  hdr->ih_ep = C0008000                                                
          goto after_header_check;                                                     
      }                                                                                
  #endif 
	然后走向了 do_bootm_linux  (cmdtp, flag, argc, argv, addr, len_ptr, verify);
	然后 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

综上, u-boot 将zImage 从nand读取到了 内存的0xC0008000,然后从0xC0008000启动,也就是 start 标号处.接下来就是压缩内核启动流程

//其他启动方式,这里不考虑 setenv bootcmd   tftp 20008000 zImage\; bootm 20008000 

zImage 的启动流程
  • 当前状态
此时zImage 在 0xC0008000 处
且当前状态为
	1.内存已经初始化好
	2.r0 = 0
	3.r1 = mach id // ok6410的 machid , 1626
	4.r2 = tags addr
	5.pc = 0xC0008000 
	// 可以看出 此时 start 标号的 运行地址 为 0xC0008000 
  • zImage的生成
  =================
  arch/arm/boot/compressed/piggy.gzip: gzip compressed data, from Unix, max compression
  cmd_arch/arm/boot/compressed/piggy.gzip := (cat arch/arm/boot/compressed/../Image | \
  gzip -n -f -9 > \
  arch/arm/boot/compressed/piggy.gzip) ||\
   (rm -f arch/arm/boot/compressed/piggy.gzip ; false)
  =================
  arch/arm/boot/compressed/vmlinux: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped
  cmd_arch/arm/boot/compressed/vmlinux := arm-linux-ld -EL    \
  --defsym zreladdr=0x50008000 \
  -p \
  --no-undefined \
  -X \
  -T arch/arm/boot/compressed/vmlinux.lds \
  arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o \
  arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o \
  arch/arm/boot/compressed/lib1funcs.o \
  -o arch/arm/boot/compressed/vmlinux 
  =================
  arch/arm/boot/zImage: Linux kernel ARM boot executable zImage (little-endian)
  cmd_arch/arm/boot/zImage := arm-linux-objcopy \
  -O binary \
  -R .comment \
  -S  arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage
  =================

可以从 vmlinux.lds 和 nm vmlinux 看出 start 标号的 链接地址 是 0x00000000  .

  • 启动流程
		//1. 将解压后的 内核的起始位置放入 r4
		ldr	r4, =zreladdr // zreladdr 大小为 0x50008000 
		...
		
		//2. 重定位过程
		// 这里考虑一个问题,重定位需要重定位什么
		// 一般一个二进制文件中的段有 这些 : .code .rodata .data .bss 
		// .code .rodata .data .bss 都需要重定位
		
		// zImage的里面有这些 .text .got .got.plt .bss .stack
		// Linux怎么实现这些段的重定位
		// - GOT 技术 解决了 .code .rodata .data .bss 的重定位
		teq	r0, #0
		 //如果delta为0,无须对GOT表项和BSS进行重定位,否则下面进行重新定向
		beq	not_relocated

		// 重定位代码,需要处理的事情
		// 使链接地址 和 运行地址 一致
		// r0 是 运行地址和链接地址的偏移量,大小等于 0xC0008000
		
		// 2.1. 为  GOT 做重定位 做准备
		
		add	r11, r11, r0
			// 计算出 GOT start 的 运行地址 ,大小等于0xC0008000 + 003ed9bc 
			// ✗ nm vmlinux  |grep _got_start 
			// 003ed9bc A _got_start 
		add	r12, r12, r0
			// 计算出 GOT end 的 运行地址,大小等于 0xC0008000 + 003ed9e8 
			// ✗ nm vmlinux  |grep _got_end   
			// 003ed9e8 A _got_end
		
		// 2.2. 为 清0 .bss段  做准备
		/*
		 * If we are running fully PIC === CONFIG_ZBOOT_ROM = n,
		 * we need to fix up pointers into the BSS region.
		 * Note that the stack pointer has already been fixed up.
		 */
		add	r2, r2, r0
		// __bss_start
		// 计算出 BSS start 的 运行地址,大小等于 0xC0008000 + 003ed9f8 
		// ✗ nm vmlinux  |grep __bss_start
		// 003ed9f8 A __bss_start
		add	r3, r3, r0
		// _end
		// 计算出 BSS end 的 运行地址,大小等于 0xC0008000 + 003eda18 
		// ✗ nm vmlinux  |grep _end
		// 003eda18 A _end
		/*
		 * Relocate all entries in the GOT table.
		 */
		 // Global Offset Table
		 // 参考 https://blog.csdn.net/hujiuding/article/details/80142334
		// 2.3. 重定位 GOT
1:		ldr	r1, [r11, #0]		@ relocate entries in the GOT
			// 加载 r11+0 地址中的数据 到 r1
		add	r1, r1, r0		@ table.  This fixes up the
			// r1 = r1 + 0xC0008000 
		str	r1, [r11], #4		@ C references.
			// 将r1 的数据保存到 r11+4 的地址中去,r11=r11+4
		cmp	r11, r12
			// 比较r11 与 r12
		blo	1b
			// 如果小于,则跳转到上面的标号1处
		// 2.4. 清0 .bss 段
not_relocated:  mov r0, #0                                                           
1:      str r0, [r2], #4        @ clear bss                                          
        str r0, [r2], #4                                                             
        str r0, [r2], #4                                                             
        str r0, [r2], #4                                                             
        cmp r2, r3                                                                   
        blo 1b


		// 3. 解压内核,并将解压后的内核放到 zreladdr 
		// 设置参数
		mov	r0, r4
		mov	r1, sp			@ malloc space above stack
		add	r2, sp, #0x10000	@ 64k max
		mov	r3, r7
		// 这里开始使用重定位的数据.
		bl	decompress_kernel
		// 4. 关cache
		bl	cache_clean_flush
		bl	cache_off
		// 5. 将控制权传给 解压后的内核.
		// 设置 启动 未压缩 kernel 所需的 r0 r1 r2
		mov	r0, #0			@ must be zero
		mov	r1, r7			@ restore architecture number
		mov	r2, r8			@ restore atags pointer
		mov	pc, r4			@ call kernel

内存分布
总述
  • 输入文件(五个.o文件)
.text .data .bss .stack 其他
  • 输出文件(arch/arm/boot/compressed/vmlinux)
.text .got .got.plt .bss .stack 其他

需要考虑 .code .rodata .data .bss .stack .heap

  • 文件中的链接地址
00000000
	.text(包括.rodata)
003edad8
	.got
003edb04
	.got.plt
003edb10
	.bss
003edb30
	.stack
003eeb30
  • 运行时的运行地址与链接地址
zImage  整个 在 C0008000 地址 处.但是链接地址是从 00000000 开始的.

zImage的第一个指令(arch/arm/boot/compressed/head.S) start 标号的第一个指令 (mov r0, r0) 位置在 C0008000 , 其链接地址在 00000000
 
 然后将关键符号的地址(链接地址) 对应的 运行地址 都 计算出来.
 	.bss 起始地址
 	.bss 结束地址
 	.got 起始地址
 	.got 结束地址 // .got.plt起始地址
 	.got.plg 结束地址
 	压缩内核结束地址
 	栈顶位置
 然后对去全局数据进行重定位(.got)
 		// 这里的代码块叫做动态链接器
 		// 动态链接器会从定位GOT表红的每个条目,使他包含正确的运行地址.
 		// 每个引用去全局数据的目标模块都有自己的GOT.
        /*                                                                           
         * Relocate all entries in the GOT table.                                    
         */                                                                          
1:      ldr r1, [r11, #0]       @ relocate entries in the GOT                        
        add r1, r1, r0      @ table.  This fixes up the                              
        str r1, [r11], #4       @ C references.                                      
        cmp r11, r12                                                                 
        blo 1b
	// 这里将 .got段(不包括.got.plt) 中的 内容 都 加了 C00008000,并放回原位.
	// .got 段 中 有 全局数据的链接地址
	// 在运行时,索引一个全局数据,是怎么索引的,对应汇编代码是什么???
	// 没有具体代码,简单来说,就是将一条指令,转换为多条指令
	// mov r0,r1(A)
	// 1. 寻找变量A,找到GOT表中对应A的条目所在的位置(运行地址)
	// 2. 获取该位置中变量A的运行地址
	// 3. 通过A的运行地址找到变量A中的值
	// 4. 将A的值放到对应的寄存器(r0)中
为什么不对函数进行重定位呢?(.got.plt)
分述
  • .code
// 也就是 输出文件中的 .text
逻辑地址
	开始地址 : 00000000
	结束地址 : 003edad8
运行地址
	开始地址 : C0008000
	结束地址 : C0008000 + 003edad8
   .text : {
    _start = .;
    *(.start)
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.rodata)
    *(.rodata.*)
    *(.glue_7)
    *(.glue_7t)
    *(.piggydata)
    . = ALIGN(4);
  }
  • .rodata
 输出文件中的 .text 包括 .rodata

  • .data
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    /*
     * Discard any r/w data - this produces a link error if we have any,
     * which is required for PIC decompression.  Local data generates
     * GOTOFF relocations, which prevents it being relocated independently
     * of the text/got segments.
     */
    *(.data)
  }
  // 为了 PIC 且 合入 GOT,  data 段 被 .got 接管了.
  • .bss
逻辑地址
	003ed9f8 - 003eda18
运行地址
	__bss_start 0xC0008000 + 003ed9f8 
	_end 0xC0008000 + 003eda18
  • .stack

链接地址
	.stack 的链接开始地址是 003edb30  ,链接结束地址是  3EEB30 ,大小 4096字节.
运行地址
	.stack 的运行开始地址是 0xC0008000 + 003edb30  ,运行结束地址是  0xC0008000 + 3EEB30 ,大小 4096字节.

_end = .;                                                                                                                                                   
. = ALIGN(8);     /* the stack must be 64-bit aligned */                          
.stack        : { *(.stack) }

$ nm vmlinux |grep _end 
003edb30 A _end



.word   .L_user_stack_end   @ sp

        .align                                                                       
        .section ".stack", "aw", %nobits                                             
.L_user_stack:  .space  4096                                                         
.L_user_stack_end:


运行地址

ldr sp, [r0, #28]
	// 此时 sp 装的是 r0+28 地址中的值, 也就是 003edb30  +C0008000


mov r1, sp          @ malloc space above stack
add r2, sp, #0x10000    @ 64k max

bl  decompress_kernel

arm 是满减栈
003edb30  +C0008000		, 里面存第一个数据,栈顶
003edb30  +C0008000 -4  , 里面存第二个数据.

  • .heap
逻辑地址
	无
运行地址
	(003edb30  +C0008000 , 003edb30  +C0008000 + 0x10000 ]

sp 中的值 为 003edb30  +C0008000

/*                                                                               
 * The C runtime environment should now be setup sufficiently.                   
 * Set up some pointers, and start decompressing.                                
 *   r4  = kernel execution address                                              
 *   r7  = architecture ID                                                           
 *   r8  = atags pointer                                                             
 */                                                                                  
        mov r0, r4                                                                   
        mov r1, sp          @ malloc space above stack                               
        add r2, sp, #0x10000    @ 64k max                                            
        mov r3, r7                                                                   
        bl  decompress_kernel

void                                                                                
decompress_kernel( \
unsigned long output_start, \  		// r0 , kernel execution address
unsigned long free_mem_ptr_p, \ 	// r1 , 栈顶 , 堆的开始
unsigned long free_mem_ptr_end_p, \ // r2 , 堆的结束                                
int arch_id);						// r3 , arch id , 1626

// r2 中的值将被 decompress_kernel 用于申请堆空间,空间大小为
(003edb30  +C0008000 , 003edb30  +C0008000 + 0x10000 ]
// 这个空间被 include/linux/decompress/mm.h 定义的 malloc 和 free 管理

  • .got 和 .got.plt
输入文件中没有,输出文件中有.
PIC技术
逻辑地址 :
	003edad8 - 003edb04
物理地址 : 
	C0008000 + 003edad8 - C0008000 + 003edb04
Image(非压缩内核)启动流程
  • 当前状态
此时Image 在 0x50008000 处
且当前状态为
	1.内存已经初始化好
	2.r0 = 0
	3.r1 = mach id // ok6410的 machid , 1626
	4.r2 = tags addr
	5.pc = 0x50008000 
之前的压缩启动流程跟现在一点关系都没有
在pc设置后,(除了当前寄存器及寄存器指向的地址中的内容)非压缩内核启动流程的代码运行完全用不到之前代码运行时建立的环境.
  • Image 的生成
  vmlinux: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, BuildID[sha1]=3a0278f4e626cd58ef56496dd843b5b17d78aca6, not stripped
  
  cmd_vmlinux := arm-linux-ld \
  -EL  \
  -p \
  --no-undefined \
  -X \
  --build-id \
  -o vmlinux \
  -T arch/arm/kernel/vmlinux.lds \
  arch/arm/kernel/head.o \
  arch/arm/kernel/init_task.o  \
  init/built-in.o \
  --start-group  \
	usr/built-in.o  \
	arch/arm/kernel/built-in.o  \
	arch/arm/mm/built-in.o  \
	arch/arm/common/built-in.o  \
	arch/arm/mach-s3c64xx/built-in.o  \
	arch/arm/plat-samsung/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  \
	arch/arm/lib/lib.a  \
	lib/lib.a  \
	arch/arm/lib/built-in.o  \
	lib/built-in.o  drivers/built-in.o  sound/built-in.o  firmware/built-in.o  net/built-in.o \
  --end-group \
  .tmp_kallsyms2.o
  =================
  arch/arm/boot/Image: data
  cmd_arch/arm/boot/Image := arm-linux-objcopy -O binary \
  -R .comment  \
  -S  vmlinux \
  arch/arm/boot/Image
  =================

  • 启动流程

1. 第一条指令
	Image 的运行地址 在 0x50008000
	Image 的起始链接地址 是 C0008000,也就是说
		1. stext标号 在 C0008000
		2. setmode宏展开的 第一句 msr cpsr_c,PSR_F_BIT | PSR_I_BIT | SVC_MODE 在 C0008000


2. 算出页表基址相关地址 (PAGE_OFFSET对应的运行地址)
算出 PAGE_OFFSET(0xC0000000) 逻辑地址 对应的 运行地址(50008000 - C0008000) + C0000000 = 50000000,并存储到r8.
为 页表基址 提供参考值

3.创建临时页表,更明了的信息请参考 表 ,表明为 临时页表内容 // 参考 https://blog.csdn.net/clb1609158506/article/details/41447475 中的图
        // 通过 pgtbl   r4, r8 算得 ,r4 = 5000 4000 ,r4 中的数据为页表基址的物理地址
        // 之后的创建页表的动作都用的是r4中的数据作为页表基址.
        
        // 注意 swapper_pg_dir 中 存储着 页表基址的虚拟理地址 0xC0004000
        1.0 页表 所在的地址空间 5000 4000-50008000
        	// 之后 开始填充这个空间
        	// 除了地址内容还有 ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags 
        	// 即 arch/arm/mm/proc-v6.S 中的 __v6_proc_info 中的
			//    ALT_UP(.long \ 
			//        PMD_TYPE_SECT | \ 
			//        PMD_SECT_AP_WRITE | \ 
			//        PMD_SECT_AP_READ | \ 
			//        PMD_FLAGS_UP)

        2. 为 从__enable_mmu到__enable_mmu_end 的代码 创建 直接映射
                // 需要映射的地址 __enable_mmu(c0008154) 到 __enable_mmu_end(c0008198) 
                
                // 页表占用的物理地址5000 5400-5000 5404 (1个页表项)

                // 映射的地址 (50000000 - 50100000) - (C0000000 - C0010000) 
                // Sections , 映射1M字节.

        3. 为 kernel 建立从虚拟地址到物理地址的映射
                // 需要映射的地址 (0x50008000-508cd554)

                // 页表占用的物理地址 5000 7000 - 5000 7024
                // 映射的地址(5000 0000 - 5090 0000) - (C000 0000 - C090 0000)
                // Sections , 映射1M字节.

        4. 为 r2 中的 bootargs 建立从虚拟地址到物理地址的映射,包括了过程1中创建的16K一级页表
                // 需要映射的地址 (r2 所在地址,r2=0x5000 0100) // 从u-boot 中查到的

                // 页表占用的物理地址 5000 7000-5000 7004 
                // 映射的地址 (5000 0000 - 5010 0000) -  (C0000000 - C0100000) 
                // Sections , 映射1M字节.


4. 开MMU
	// 在这一步已经做好了重定位
	// 下面的指令 中 链接地址就是虚拟地址,大小一样.下面的地址最好不要称呼为链接地址,最好称呼为虚拟地址
	// 访问虚拟地址时,MMU会自动做转换.
5.
__mmap_switched
	//0. 如何进入 __mmap_switched
	// 开启 MMU 之前, ldr r13, =__mmap_switched
	// 开启 MMU 之后, mov r3, r13; mov pc, r3
	// 根据 (2. 为 从__enable_mmu到__enable_mmu_end 的代码 创建 直接映射) 访问内存
	// 进入 __mmap_switched 之前的最后的一条指令
	// 做 mov pc, r3 的动作的时候,是需要MMU访存流程的.
	// 即 取值 : 把 r13(__mmap_switched) 中的地址的指令 (adr r3, __mmap_switched_data,实际是条伪指令,会编译成多条指令)给CPU
	// 这时,就需要 走 MMU开启后的 内存访问流程.
	// 1.给出的 __mmap_switched 地址 此时变成了虚拟地址(原来是链接地址) ,可以通过 System.map找到 虚拟地址c00081e0
	// 2.寻找对应的物理地址 , tlb中找不到物理地址,访问 临时页表 (3. 为 kernel 建立从虚拟地址到物理地址的映射), 找到 虚拟地址c00081e0 对应的物理地址 500081e0 
	// 3.寻找物理地址中的值,值为 (adr r3, __mmap_switched_data)的二进制代码.
	1. 搬运数据段
		// 从 __data_loc 搬移到 _sdata
		// 判断数据段存储地址(r4,__data_loc )和数据段起始地址(r5,_sdata)
		// 如果不一样的话需要搬移到数据段起始地址上
		// 本例中没有搬移
	2. 清空bss段
	3. 赋值变量 
		cpu id
			arch/arm/kernel/setup.c 中的 unsigned int processor_id
		机器 id
			arch/arm/boot/compressed/misc.c 中的 unsigned int __machine_arch_type
		tags 首地址
			arch/arm/kernel/setup.c 中的 unsigned int __atags_pointer __initdata
		cp15 中的 寄存器1 的 架构相关寄存器的值
			arch/arm/kernel/entry-armv.S 中的 cr_alignment
		cp15 中的 寄存器1 的 架构相关寄存器的值 & A = 0
			arch/arm/kernel/entry-armv.S 中的 cr_no_alignment
	4. 设置sp
		sp = init_thread_union(c0802000) + 8184
		   = 0xC0803FF8
6.
b start_kernel
	跳转到 start_kernel

内存分布
总述
做了几件事
// 此时没有虚拟地址,只有运行地址和链接地址
1.创建临时页表
2.开MMU
// 有虚拟地址和链接地址和物理地址
// 在开MMU的情况下,怎么理解 3 4 5 过程
3.搬运数据段
	// 其实没做
4.清空bss段
5.设置sp

内存重定位也是存在的,只不过过程和 压缩内核启动流程 不一样
在这里,内存重定位过程如下
1.创建临时页表
2.打开MMU
// 此时内存重定位已经完成.
3.访问指令和数据

在 压缩内核启动流程 中
	重定位前, 访问 全局符号 时,因为全局符号是链接地址,所以要将链接地址转换为运行地址(运行的物理地址)
	重定位技术: GOT
	重定位后,所有的全局符号都已经是运行地址,直接访问,直接给出(指令或数据)
在 非压缩内核启动流程中
	重定位前, 访问 全局符号 时,因为全局符号是链接地址,所以要将链接地址转换为运行地址(运行的物理地址)
	重定位技术: MMU
	重定位后, 访问全局符号 时,因为全局符号时虚拟地址,MMU自动将虚拟地址转换为运行地址,直接给出(指令或数据)
分述
需要考虑 .code .rodata .data .bss .stack .heap ,

其实在 MMU 开启的那一刻,整个 Image 都已经重定位了
所以其中的 .code .rodata .data .bss 在那一刻都完成了重定位.

.stack 是在 MMU 打开之后,通过设置sp建立的,大小为0xC0803FF8
.heap  还没建立

其他
  • 临时页表内容
__create_page_tables功能页表起始地址页表结束地址页表项个数物理地址起始物理地址结束物理地址范围映射关系虚拟地址起始虚拟地址结束虚拟地址范围
过程1清0地址空间5000 40005000 80004096个
过程2为 从__enable_mmu(c0008154) 到 __enable_mmu_end(c0008198) 的代码 创建 映射5000 54005000 54041个5000 00005010 00001M线性映射(直接相等)5000 00005010 00001M
过程3为 整个 Image 创建映射5000 70005000 70249个5000 00005090 00009M线性映射C000 0000C090 00009M
过程4为atags所在空间创建映射5000 70005000 70041个5000 00005010 00001M线性映射C000 0000C010 00001M
其他过程4覆盖了 过程3创建的第一个页表项,页表起始地址(5000 7000)不是随便找的,能保证 虚拟地址C000 81e0(__mmap_switched) 与 translation table base 5000 4000 结合能找到 一级描述符 地址 5000 7000 ,且 50007000 地址 中的内容 与 虚拟地址C000 81e0 结合 能找到 物理地址 5000 81e0(__mmap_switched)
  • arch/arm/boot/compressed/vmlinux 的段
$ readelf  -S arch/arm/boot/compressed/vmlinux
There are 20 section headers, starting at offset 0x3fbd44:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 008000 3edad8 00  AX  0   0 32
  [ 2] .got              PROGBITS        003edad8 3f5ad8 00002c 00  WA  0   0  4
  [ 3] .got.plt          PROGBITS        003edb04 3f5b04 00000c 04  WA  0   0  4
  [ 4] .bss              NOBITS          003edb10 3f5b10 000020 00  WA  0   0  4
  [ 5] .stack            NOBITS          003edb30 3f5b10 001000 00  WA  0   0  1
  [ 6] .comment          PROGBITS        00000000 3f5b10 000054 00      0   0  1
  [ 7] .ARM.attributes   ARM_ATTRIBUTES  00000000 3f5b64 000029 00      0   0  1
  [ 8] .debug_line       PROGBITS        00000000 3f5b8d 001049 00      0   0  1
  [ 9] .debug_info       PROGBITS        00000000 3f6bd6 001906 00      0   0  1
  [10] .debug_abbrev     PROGBITS        00000000 3f84dc 000618 00      0   0  1
  [11] .debug_aranges    PROGBITS        00000000 3f8af8 000088 00      0   0  8
  [12] .debug_ranges     PROGBITS        00000000 3f8b80 0004c0 00      0   0  8
  [13] .debug_frame      PROGBITS        00000000 3f9040 000288 00      0   0  4
  [14] .debug_loc        PROGBITS        00000000 3f92c8 00216c 00      0   0  1
  [15] .debug_pubnames   PROGBITS        00000000 3fb434 0001a4 00      0   0  1
  [16] .debug_str        PROGBITS        00000000 3fb5d8 0006a1 01  MS  0   0  1
  [17] .shstrtab         STRTAB          00000000 3fbc79 0000ca 00      0   0  1
  [18] .symtab           SYMTAB          00000000 3fc064 000c90 10     19 159  4
  [19] .strtab           STRTAB          00000000 3fccf4 000595 00      0   0  1
  • 临时页表推演过程
/*
 * Setup the initial page tables.  We only setup the barest
 * amount which are required to get the kernel running, which
 * generally means mapping in the kernel code.
 *
 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
 *
 * Returns:
 *  r0, r3, r5-r7 corrupted
 *  r4 = physical page table address
 */
__create_page_tables:
	pgtbl	r4, r8				@ page table address
    // r4 = 5000 4000


    // 1. 清0 5000 4000 到 5000 8000



	/*
	 * Clear the 16K level 1 swapper page table
	 */
	mov	r0, r4
    // r0 = 5000 4000
	mov	r3, #0
    // r3 = 0
	add	r6, r0, #0x4000
    // r6 = 5000 8000
1:	str	r3, [r0], #4
    // 存 0 到 地址 5000 4000
    // r0 = r0 + 4 = 5000 4004
	str	r3, [r0], #4
    // 存 0 到 地址 5000 4004
    // r0 = r0 + 4 = 5000 4008
	str	r3, [r0], #4
    // 存 0 到 地址 5000 4008
    // r0 = r0 + 4 = 5000 400C
	str	r3, [r0], #4
    // 存 0 到 地址 5000 400C
    // r0 = r0 + 4 = 5000 4010
	teq	r0, r6
	bne	1b





    // 2. 5000 5400 地址处 存储 一个页表项 5000 5400-5000 5404
        // phy  : 5000 0000
        // size :   10 0000
        // 功能 : 映射 __enable_mmu(c0008154) 到 __enable_mmu_end(c0008198) 的代码




	ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
    // 映射种类为 section , 大小为 10 0000

	/*
	 * Create identity mapping to cater for __enable_mmu.
	 * This identity mapping will be removed by paging_init().
	 */
	adr	r0, __enable_mmu_loc
    // r0 中 装入 __enable_mmu_loc 的 物理地址
	ldmia	r0, {r3, r5, r6}
    // r3 中 装入 __enable_mmu_loc 的 逻辑地址
    // r5 中 装入 __enable_mmu     的 逻辑地址 , c0008154
    // r6 中 装入 __enable_mmu_end 的 逻辑地址 , c0008198
	sub	r0, r0, r3			@ virt->phys offset
    // r0 = r0 - r3 = (物理地址 - 逻辑地址) // 50008000-C0008000
	add	r5, r5, r0			@ phys __enable_mmu
    // r5 = r5 + r0 = __enable_mmu 的 物理地址 , 50008154
	add	r6, r6, r0			@ phys __enable_mmu_end
    // r6 = r6 + r0 = __enable_mmu_end 的 物理地址 , 50008198
	mov	r5, r5, lsr #20
    // r5 = r5 >> 20 = 0x500
	mov	r6, r6, lsr #20
    // r6 = r6 >> 20 = 0x500

1:	orr	r3, r7, r5, lsl #20		@ flags + kernel base
    // r3 = r7 | (r5 << 20) = 5000 0000 | flags
	str	r3, [r4, r5, lsl #2]		@ identity mapping
    // 存 r3 到 r4 + (r5 << 2) 即 存 (5000 0000 | flags) 到 5000 4000 + 1400 = 5000 5400
    // 不重新计算r4
	teq	r5, r6
    // r5 == r6
	addne	r5, r5, #1			@ next section
    // 不做
	bne	1b
    // 不做



    // 3. 5000 7000 地址处 存储 9((0x5000 7023- 0x5000 7000)/4 + 1) 页表项 5000 7000 - 5000 7024
        // 1.
        // phy  : 5000 0000
        // size :   10 0000
        // 2.
        // phy  : 5010 0000
        // size :   10 0000
        // 3.
        // phy  : 5020 0000
        // size :   10 0000
        // 4.
        // phy  : 5030 0000
        // size :   10 0000
        // 5.
        // phy  : 5040 0000
        // size :   10 0000
        // 6.
        // phy  : 5050 0000
        // size :   10 0000
        // 7.
        // phy  : 5060 0000
        // size :   10 0000
        // 8.
        // phy  : 5070 0000
        // size :   10 0000
        // 9.
        // phy  : 5080 0000
        // size :   10 0000



	/*
	 * Now setup the pagetables for our kernel direct
	 * mapped region.
	 */
	mov	r3, pc
    // 当前pc 懒得算 ,拿 __create_page_tables(c000804c)来算,对应的运行地址 是 5000804c
    // r3 = 5000804c
	mov	r3, r3, lsr #20
    // r3 = 500
	orr	r3, r7, r3, lsl #20
    // r3 = 5000 0000 | r7

	add	r0, r4,  #(KERNEL_START & 0xff000000) >> 18
    // r0 = 5000 4000 + (0xC0008000 & 0xff000000 ) >> 0x12
    // r0 = 5000 4000 + 3000
    // r0 = 5000 7000

	str	r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
    // 存 (5000 0000 | r7) 到 5000 7000 + (0xC0008000 & 0x00f00000) >> 18
    // 存 (5000 0000 | r7) 到 5000 7000 + 0
    // 不重新计算r0

	ldr	r6, =(KERNEL_END - 1)
    // r6 = c08cd554-1 = c08cd553
	add	r0, r0, #4
    // r0 = 5000 B400 + 4
	add	r6, r4, r6, lsr #18
    // r6 = 5000 4000 + c08cd553 << 0x12
    // r6 = 5000 4000 + 3023
    // r6 = 5000 7023
1:	cmp	r0, r6
    // 比较 5000 7000 与 5000 7023
	add	r3, r3, #1 << 20
    // r3 = r3 + 10 0000
	strls	r3, [r0], #4
    // 如果 r0 < r6
    // 存 r3 到 r0
    // r0 = r0 + 4
	bls	1b

#ifdef CONFIG_XIP_KERNEL
	/*
	 * Map some ram to cover our .data and .bss areas.
	 */
	add	r3, r8, #TEXT_OFFSET
	orr	r3, r3, r7
	add	r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
	str	r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
	ldr	r6, =(_end - 1)
	add	r0, r0, #4
	add	r6, r4, r6, lsr #18
1:	cmp	r0, r6
	add	r3, r3, #1 << 20
	strls	r3, [r0], #4
	bls	1b
#endif

    // 3. 5000 7000 地址处 存储 一个页表项 5000 7000-5000 7004
        // phy  : 5000 0000
        // size :   10 0000

	/*
	 * Then map boot params address in r2 or
	 * the first 1MB of ram if boot params address is not specified.
	 */

    #board/samsung/smdk6410/smdk6410.c
    #	gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
    #
    #include/configs/smdk6410.h
    #	#define PHYS_SDRAM_1        MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */
    #	#define MEMORY_BASE_ADDRESS 0x50000000

    // r2 = 0x5000 0100
	mov	r0, r2, lsr #20
    // r0 = r2 >> 20 = 0x500
	movs	r0, r0, lsl #20
    // r0 = r0 << 20 = 0x5000 0000
	moveq	r0, r8
    // Z为没置位,该句不执行
	sub	r3, r0, r8
    // r3 = r0 - r8 = 0x5000 0000 - 0x 5000 0000 = 0
	add	r3, r3, #PAGE_OFFSET
    // r3 = r3 + 0xC000 0000 = 0xC000 0000
	add	r3, r4, r3, lsr #18
    // r3 = r4 + 0xC0000000 >> 18
    // r3 = 5000 4000 + 0x3000 = 0x5000 7000
	orr	r6, r7, r0
    // r6 = r7 | r0
    // r6 = r7 | 0x5000 0000
	str	r6, [r3]
    // 存 0x5000 0000 到 地址 0x5000 7000

#ifdef CONFIG_DEBUG_LL
#ifndef CONFIG_DEBUG_ICEDCC
	/*
	 * Map in IO space for serial debugging.
	 * This allows debug messages to be output
	 * via a serial console before paging_init.
	 */
	addruart r7, r3

	mov	r3, r3, lsr #20
	mov	r3, r3, lsl #2

	add	r0, r4, r3
	rsb	r3, r3, #0x4000			@ PTRS_PER_PGD*sizeof(long)
	cmp	r3, #0x0800			@ limit to 512MB
	movhi	r3, #0x0800
	add	r6, r0, r3
	mov	r3, r7, lsr #20
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
	orr	r3, r7, r3, lsl #20
1:	str	r3, [r0], #4
	add	r3, r3, #1 << 20
	teq	r0, r6
	bne	1b

#else /* CONFIG_DEBUG_ICEDCC */
	/* we don't need any serial debugging mappings for ICEDCC */
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
#endif /* !CONFIG_DEBUG_ICEDCC */

#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
	/*
	 * If we're using the NetWinder or CATS, we also need to map
	 * in the 16550-type serial port for the debug messages
	 */
	add	r0, r4, #0xff000000 >> 18
	orr	r3, r7, #0x7c000000
	str	r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
	/*
	 * Map in screen at 0x02000000 & SCREEN2_BASE
	 * Similar reasons here - for debug.  This is
	 * only for Acorn RiscPC architectures.
	 */
	add	r0, r4, #0x02000000 >> 18
	orr	r3, r7, #0x02000000
	str	r3, [r0]
	add	r0, r4, #0xd8000000 >> 18
	str	r3, [r0]
#endif
#endif
	mov	pc, lr
ENDPROC(__create_page_tables)
	.ltorg
	.align
__enable_mmu_loc:
	.long	.
	.long	__enable_mmu
	.long	__enable_mmu_end
问题,为什么 vmlinux 里面有 .stack 段

arch/arm/boot/compressed/head.S
1115         .align                                                                   
1116         .section ".stack", "aw", %nobits                                         
1117 .L_user_stack:  .space  4096                                                     
1118 .L_user_stack_end:
  • GOT技术
arch/arm/boot/compressed/vmlinux
	_got_start 003edad8
	*(.got)
	_got_end 003edb04
	*(.got.plt)
	_edata 003edb10

arch/arm/boot/compressed/vmlinux 生成过程
  -T arch/arm/boot/compressed/vmlinux.lds \
  arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o \
  arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o \
  arch/arm/boot/compressed/lib1funcs.o \
  -o arch/arm/boot/compressed/vmlinux

五个.o 里面都没有 .got 和 .got.plt 段,为什么 vmlinux就有这个段了呢???


深入理解计算机系统第三版 7.12 与位置无关的代码(PIC)
程序员自我修养第七章 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值