zoukankan      html  css  js  c++  java
  • 第13课.代码重定位

    1.重定位的引入

    NOR FLASH 可以像内存一样的读,但不能像内存一样的写。无法直接去修改全局变量和静态变量
    NAND FLASH 把前面4k的代码放入SRAM。如果程序大于4k时,前面4k的代码需要把整个程序读出来
    针对以上情况,引入重定位
    

    程序结构

    代码段:text
    数据段(全局变量):data
    只读全局变量(const):rodata
    初始值为0,或者无初始值的全局变量:bss
    注释段:common
    

    注:

    bss和common段不保存在bin中
    

    2.连接脚本的改进

    SECTIONS {
    ...
    secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
      { contents } >region :phdr =fill
    ...
    }
    
    secname和contents是必须的,其他可选
    secname:段名,用来命名此段
    contents:决定哪些内容放在本段,可以是整个目标文件(.o)也可以是目标文件中的某段(代码段,数据段等)。start.o或者这样start.o *(.text)
    start:是段的重定位地址,即本段运行的地址。如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上。start可以用在任意一种描述地址的符号来描述
    BLOCK(align)指定快对齐。比如,前面一个段从0x30000000到0x300003F1此处标记ALIGN(4),表示此处最小占用4Bytes,即使下一个段是紧挨这个段,那么下一个段的起始位置(也就是运行地址)为0x0x300003F4。
    NOLOAD:告诉加载器程序运行时不加载该段到内存
    AT(ldadr):定义本段存储的地址,如果不使用这个选项,则加载地址等于运行地址,通过这个选项可以控制隔断分别保存于输出文件中不同的位置。
    

    3.简单链接脚本讲解

    Makefile

    arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
    
    1.使用链接脚本时,它将所以的.o文件生成了一个.elf格式的文件。含有地址信息(load addr)
    2.使用加载器。把elf文件读入内存(读到load addr)
        对裸板,加载器是JTAG调试工具
        对APP,加载器也是APP
    3.运行
    4.如果load addr != runtime addr程序本身要重定位
    

    4.通过汇编来实现重定位

    重定位的流程

    1.代码被编译成一个bin文件
    2.bin文件烧写到flash中
    3.flash中的代码被重定位(拷贝)到sdram中
    启动开发板时,代码先从flash上运行
    

    重定位的两种方式

    A.只重定位数据段(适用于单片机)

    说明:

    代码段和数据段运行时的地址是分开的
    代码段运行时的地址是 0
    数据段运行时的地址是 0x30000000
    

    Makefile

    arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
    

    sdram.lds

    SECTIONS{
    	.text 0 : {*(.text)}
    	.rodata : {*(.rodata)}
    	.data 0x30000000 : AT(0x800)
    	{
    		data_load_addr = LOADADDR(.data);
    		. = ALIGN(4);
    		data_start = . ;
    		*(.data)
    		data_end = . ;
    	}
    
    	. = ALIGN(4);
    	bss_start = .;
    	.bss : {*.(bss) *(.COMMON)}
    	bss_end = .;
    }
    

    start.S

    	bl sdram_init    /*    初始化sdram    */
    
    	/*	重定位data段	*/
    	ldr r1, =data_load_addr
    	ldr r2, =data_start
    	ldr r3, =data_end
    
    cpy:
    	ldr r4, [r1]
    	str r4, [r2]
    	add r1, r1, #4
    	add r2, r2, #4
    	cmp r2, r3
    	ble cpy			/*	小于或等于跳转	*/
    
    	/*	清楚bss段	*/
    	ldr r1, =bss_start
    	ldr r2, =bss_end
    	mov r3, #0
    
    clean:
    	str r3, [r1]
                add r1, r1, #4
    	cmp r1, r2
    	ble clean
    

    B.重定位整个程序(常用)

    说明:

    代码段和数据段是一体的
    

    注意事项

    1.链接脚本中指定runtime addr为SDRAM
    2.重定位之前的代码与位置无关(此时代码在flash上运行),用位置无关码写成
    注:重定位拷贝代码后应该有两份代码,一份在flash中,一份在SDRAM中
    

    sdram.lds

    SECTIONS{
    	. = 0x30000000;
    	
    	__code_start = .;
    	. = ALIGN(4);
    	.text : {*(.text)}
    	
    	. = ALIGN(4);
    	.rodata : {*(.rodata)}
    	
    	. = ALIGN(4);
    	.data : {*(.data)}
    	
    	. = ALIGN(4);
    	__bss_start = .;
    	.bss : { *(.bss) *(.COMMON) }
    	_end = .;
    }
    

    start.S

    	bl sdram_init
    
    	/*	重定位text, rodata, data段	*/
    	mov r1, #0
    	ldr r2, =_start
    	ldr r3, =_bss_start
    
    cpy:
    	ldr r4, [r1]
    	str r4, [r2]
    	add r1, r1, #4
    	add r2, r2, #4
    	cmp r1, r2
    	ble cpy
    
    	/*	清楚bss段	*/
    	ldr r1, =_bss_start
    	ldr r2, = _end
    	mov r3, #0
    
    clean:
    	str r3, [r1]
    	add r1, r1, #4
    	cmp r1, r2
    	ble clean
    

    5.相对跳转,绝对跳转

    相对跳转:b/bl
    说明:相对跳转,它是相对于当前运行时所处的环境而言的。比如说现在在NorFlash运行代码,则会跳到NorFlash上去执行代码。而不会去SDRAM上去执行代码
    eg:
        bl main
    
    绝对跳转:ldr pc, =???
    当NorFlash上完成了SDRAM的初始化和重定位后,就可以使用绝对跳转。使程序从NorFlash上跳转到SDRAM上。
    eg:
        ldr pc, =main
    

    图解

    注释:
        黑色为相对跳转
        红色为绝对跳转
    

    注意事项

    重定位之前,不可使用绝对地址,即不可访问全局变量,静态变量,不可访问有初始值的数组。因为这些放在data段。
    

    6.c语言来实现SDRAM重定位

    知识补充

    1.C程序中不保存lds文件中的变量
    2.借助symbol table保存lds文件中的变量,使用时加上"&"得到它的地址从而得到它的值
    

    start.S

    /* 重定位text, rodata, data段整个程序 */
    bl copy2sdram
    
    /* 清除BSS段 */
    bl clean_bss
    

    init.c

    void copy2sdram(void)
    {
    	extern int __code_start, __bss_start;
    
    	volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
    	volatile unsigned int *end  = (volatile unsigned int *)&__bss_start;
    	volatile unsigned int *src = 0;
    	
    	while(dest < end)
    	{
    		*dest++ = *src++;
    	}
    }
    
    void clean_bss(void)
    {
    	extern int _end, __bss_start;
    
    	volatile unsigned int *start  = (volatile unsigned int *)&__bss_start;
    	volatile unsigned int *dest = (volatile unsigned int *)&_end;
    
    	while(start < dest)
    	{
    		*start++ = 0;
    	}
    }
  • 相关阅读:
    Django之admin
    Django之CSRF(跨站请求伪造)
    Django之Cookie
    Django之Session
    Django|第一部
    简单的udp消息收发
    .net core里使用ado.net访问sqlserver数据库
    CentOS8中安装SQLServer
    CentOS里配置.net core运行环境(含后台运行,附ssh长连接)
    发布"dotNet core"到CentOS8
  • 原文地址:https://www.cnblogs.com/huangdengtao/p/12165037.html
Copyright © 2011-2022 走看看