linux内存管理 (三) 5 arm-linux启动过程地址相关概念及处理方法

ARM地址

  • 虚拟地址
开MMU的时候
	CPU发出的地址是虚拟地址
		PC寄存器中的值是虚拟地址
	输出给主存的地址是物理地址
	输出给cache 索引的地址是物理地址或虚拟地址
	输出给MMU/TLB的地址 是虚拟地址

  • 物理地址
物理地址在 地址总线上有体现
物理地址在 内存实体上有体现

在没开MMU的时候
	不存在虚拟地址
	所有的运行地址都是物理地址
		PC寄存器中的值是物理地址
  • 链接地址
1.链接过程中的体现
	1.1
		arm-linux-ld 的参数 -Ttext 定义链接地址,
		arm-linux-ld -Ttext 0x3000000 -g -o led_on_elf  crt0.o led_lighton.o
	1.2 
		链接脚本里有体现

2.可执行文件中关于 链接地址的体现
	1.
		第一条汇编指令代码会有一个链接地址,就是0x3000000 
		第二条汇编指令代码会有一个链接地址,就是0x3000004
	2.
		如果有地址相关码,例如 ldr pc,=lable 的话,编译生成的汇编指令中会包含 连接地址

			如果汇编指令指令代码的 链接地址 == 汇编指令代码的 运行地址(运行时的物理地址)
				不会产生问题
			如果汇编指令指令代码的 链接地址 != 汇编指令代码的 运行地址
				1. 启动的时候,pc=代码的运行地址,然后按照 pc = pc + 4 运行下去
					此时虽然 连接地址 != 运行地址 , 但是没问题
				2. 一旦遇到 地址相关码,例如 ldr pc,=0x30000010, 且 连接地址 0x30000010 处 为 lable
					就会将 物理地址 0x30000010 处的 内容(不知道是什么) 取值,译码,执行
					
					此时 如果 运行地址 0x30000010 和 连接地址 0x30000010 一致
						就没问题,因为运行地址 0x30000010 处的内容就是 标号 lable 的内容
					此时 如果 运行地址 0x30000010 和 连接地址 0x30000010 一致
						就会产生问题,就会产生问题,因为运行地址 0x30000010 处的内容不是 标号 lable 的内容 


  • 运行地址
代码在运行时
	代码的物理地址
	数据的物理地址

地址概念相关代码

  • 位置无关码

PIC : position independent code


当运行地址不在链接地址时,必须用位置无关码,如果用位置相关码,会出问题.


 bl lable . b lable 是位置无关码的体现.
 
运行完这一句后 pc = pc + 偏移值 ,而这个偏移值 是编译器算好的.
可以从汇编程序中看出来 , 汇编的到的数字中可以看出来这个偏移值

这个算出来的pc可能不予lable的连接地址相同.所以称为位置无关码

怎么用位置无关码
	1/跳转用b 或者bl
	2/C语言中不用全局 或者 静态


// 更详细的内容可以 搜索关键字 韦东山 位置无关码
  • 位置相关码

//ldr pc , =lable

将pc赋值为 lable 的链接地址 ,就是反汇编 后 .dis中的第一列的地址.

这个pc是绝对地址

怎么获取各种地址

  • 怎么获取运行地址(adr r0, LC0)与链接地址(System.map和lds连接脚本中的符号地址都是链接地址)

        .align  2                                                                
        .type   LC0, #object                                                     
LC0:    .word   LC0         @ r1                                                 
        .word   __bss_start     @ r2                                                 
        .word   _end            @ r3                                                 
        .word   _edata          @ r6                                                 
        .word   input_data_end - 4  @ r10 (inflated size location)                   
        .word   _got_start      @ r11                                                
        .word   _got_end        @ ip                                                 
        .word   .L_user_stack_end   @ sp                                             
        .size   LC0, . - LC0


restart:    
		adr r0, LC0
			// 将标号 LC0 的地址放入 r0
			// r0 : LC0运行地址
        ldmia   r0, {r1, r2, r3, r6, r10, r11, r12}
        	// .word   LC0 在运行前就已经分配好了LC0的大小和值,值是连接地址
			// r1 : LC0链接地址
			// r6 : _edata链接地址
        ldr sp, [r0, #28]                                                        
                                                                                 
        /*                                                                       
         * We might be running at a different address.  We need                  
         * to fix up various pointers.                                           
         */                                                                      
        sub r0, r0, r1      @ calculate the delta offset  
        	// 获取     LC0运行地址 -    LC0链接地址 的差值                
        add r6, r6, r0      @ _edata
        	// 计算出 _edata 的 运行地址




  • 怎么获取当前运行代码的运行地址

pc寄存器中的值-4

地址概念相关代码功能

  • 重定位(Relocate)
运行地址和链接地址不一致时,需要重定位
实现方式
	1. 将代码从 当前物理地址 搬移到 链接地址

参考

  • 嵌入式Linux:物理地址、链接地址、虚拟地址的区别

  • 物理地址,虚拟地址,链接地址的个人理解

  • PIC位置无关代码

你可能感兴趣的:(Linux内存管理)