ld链接文件关系到程序的代码段数据段bss段及其用户自定义段的运行位置,ld文件中的各个段都会在main函数之前,从加载域拷贝到运行域中。本章将具体介绍如何修改ld和startup文件。
软件平台:VSCODE+GCC工具链
硬件平台:rt1176开发板
分析 MIMXRT1176xxxxx_cm7_flexspi_nor.ld 文件
官方原始文件如下:
/* Entry Point */
ENTRY(Reset_Handler)HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400;
RPMSG_SHMEM_SIZE = DEFINED(__use_shmem__) ? 0x2000 : 0;
VECTOR_RAM_SIZE = DEFINED(__ram_vector_table__) ? 0x00000400 : 0;
TEXT_SIZE = DEFINED(__use_flash64MB__) ? 0x03FBDC00 : 0x00FBDC00;
CORE1IMAGE_START = DEFINED(__use_flash64MB__) ? 0x33FC0000 : 0x30FC0000;/* Specify the memory areas */
MEMORY
{m_flash_config (RX) : ORIGIN = 0x30000400, LENGTH = 0x00000C00m_ivt (RX) : ORIGIN = 0x30001000, LENGTH = 0x00001000m_interrupts (RX) : ORIGIN = 0x30002000, LENGTH = 0x00000400m_text (RX) : ORIGIN = 0x30002400, LENGTH = TEXT_SIZEm_qacode (RX) : ORIGIN = 0x00000000, LENGTH = 0x00040000m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00040000m_data2 (RW) : ORIGIN = 0x202C0000 + RPMSG_SHMEM_SIZE, LENGTH = 0x00080000 - RPMSG_SHMEM_SIZErpmsg_sh_mem (RW) : ORIGIN = 0x202C0000, LENGTH = RPMSG_SHMEM_SIZEm_core1_image (RX) : ORIGIN = CORE1IMAGE_START, LENGTH = 0x00040000
}/* Define output sections */
SECTIONS
{__NCACHE_REGION_START = ORIGIN(rpmsg_sh_mem);__NCACHE_REGION_SIZE = LENGTH(rpmsg_sh_mem);.flash_config :{. = ALIGN(4);__FLASH_BASE = .;KEEP(* (.boot_hdr.conf)) /* flash config section */. = ALIGN(4);} > m_flash_configivt_begin = ORIGIN(m_flash_config) + LENGTH(m_flash_config);.ivt : AT(ivt_begin){. = ALIGN(4);KEEP(* (.boot_hdr.ivt)) /* ivt section */KEEP(* (.boot_hdr.boot_data)) /* boot section */KEEP(* (.boot_hdr.dcd_data)) /* dcd section */. = ALIGN(4);} > m_ivt/* section for storing the secondary core image */.core1_code :{. = ALIGN(4) ;KEEP (*(.core1_code))*(.core1_code*). = ALIGN(4) ;} > m_core1_image/* NOINIT section for rpmsg_sh_mem */.noinit_rpmsg_sh_mem (NOLOAD) : ALIGN(4){__RPMSG_SH_MEM_START__ = .;*(.noinit.$rpmsg_sh_mem*). = ALIGN(4) ;__RPMSG_SH_MEM_END__ = .;} > rpmsg_sh_mem/* The startup code goes first into internal RAM */.interrupts :{__VECTOR_TABLE = .;__Vectors = .;. = ALIGN(4);KEEP(*(.isr_vector)) /* Startup code */. = ALIGN(4);} > m_interrupts/* The program code and other data goes into internal RAM */.text :{. = ALIGN(4);*(.text) /* .text sections (code) */*(.text*) /* .text* sections (code) */*(.rodata) /* .rodata sections (constants, strings, etc.) */*(.rodata*) /* .rodata* sections (constants, strings, etc.) */*(.glue_7) /* glue arm to thumb code */*(.glue_7t) /* glue thumb to arm code */*(.eh_frame)KEEP (*(.init))KEEP (*(.fini)). = ALIGN(4);} > m_text.ARM.extab :{*(.ARM.extab* .gnu.linkonce.armextab.*)} > m_text.ARM :{__exidx_start = .;*(.ARM.exidx*)__exidx_end = .;} > m_text.ctors :{__CTOR_LIST__ = .;/* gcc uses crtbegin.o to find the start ofthe constructors, so we make sure it isfirst. Because this is a wildcard, itdoesn't matter if the user does notactually link against crtbegin.o; thelinker won't look for a file to match awildcard. The wildcard also means that itdoesn't matter which directory crtbegin.ois in. */KEEP (*crtbegin.o(.ctors))KEEP (*crtbegin?.o(.ctors))/* We don't want to include the .ctor section fromfrom the crtend.o file until after the sorted ctors.The .ctor section from the crtend file contains theend of ctors marker and it must be last */KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))KEEP (*(SORT(.ctors.*)))KEEP (*(.ctors))__CTOR_END__ = .;} > m_text.dtors :{__DTOR_LIST__ = .;KEEP (*crtbegin.o(.dtors))KEEP (*crtbegin?.o(.dtors))KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors))KEEP (*(SORT(.dtors.*)))KEEP (*(.dtors))__DTOR_END__ = .;} > m_text.preinit_array :{PROVIDE_HIDDEN (__preinit_array_start = .);KEEP (*(.preinit_array*))PROVIDE_HIDDEN (__preinit_array_end = .);} > m_text.init_array :{PROVIDE_HIDDEN (__init_array_start = .);KEEP (*(SORT(.init_array.*)))KEEP (*(.init_array*))PROVIDE_HIDDEN (__init_array_end = .);} > m_text.fini_array :{PROVIDE_HIDDEN (__fini_array_start = .);KEEP (*(SORT(.fini_array.*)))KEEP (*(.fini_array*))PROVIDE_HIDDEN (__fini_array_end = .);} > m_text__etext = .; /* define a global symbol at end of code */__DATA_ROM = .; /* Symbol is used by startup for data initialization */.interrupts_ram :{. = ALIGN(4);__VECTOR_RAM__ = .;__interrupts_ram_start__ = .; /* Create a global symbol at data start */*(.m_interrupts_ram) /* This is a user defined section */. += VECTOR_RAM_SIZE;. = ALIGN(4);__interrupts_ram_end__ = .; /* Define a global symbol at data end */} > m_data__VECTOR_RAM = DEFINED(__ram_vector_table__) ? __VECTOR_RAM__ : ORIGIN(m_interrupts);__RAM_VECTOR_TABLE_SIZE_BYTES = DEFINED(__ram_vector_table__) ? (__interrupts_ram_end__ - __interrupts_ram_start__) : 0x0;.data : AT(__DATA_ROM){. = ALIGN(4);__DATA_RAM = .;__data_start__ = .; /* create a global symbol at data start */*(m_usb_dma_init_data)*(.data) /* .data sections */*(.data*) /* .data* sections */*(DataQuickAccess) /* quick access data section */KEEP(*(.jcr*)). = ALIGN(4);__data_end__ = .; /* define a global symbol at data end */} > m_data__ram_function_flash_start = __DATA_ROM + (__data_end__ - __data_start__); /* Symbol is used by startup for TCM data initialization */.ram_function : AT(__ram_function_flash_start){. = ALIGN(32);__ram_function_start__ = .;*(CodeQuickAccess). = ALIGN(128);__ram_function_end__ = .;} > m_qacode__NDATA_ROM = __ram_function_flash_start + (__ram_function_end__ - __ram_function_start__);.ncache.init : AT(__NDATA_ROM){__noncachedata_start__ = .; /* create a global symbol at ncache data start */*(NonCacheable.init). = ALIGN(4);__noncachedata_init_end__ = .; /* create a global symbol at initialized ncache data end */} > m_data. = __noncachedata_init_end__;.ncache :{*(NonCacheable). = ALIGN(4);__noncachedata_end__ = .; /* define a global symbol at ncache data end */} > m_data__DATA_END = __NDATA_ROM + (__noncachedata_init_end__ - __noncachedata_start__);text_end = ORIGIN(m_text) + LENGTH(m_text);ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")/* Uninitialized data section */.bss :{/* This is used by the startup in order to initialize the .bss section */. = ALIGN(4);__START_BSS = .;__bss_start__ = .;*(m_usb_dma_noninit_data)*(.bss)*(.bss*)*(COMMON). = ALIGN(4);__bss_end__ = .;__END_BSS = .;} > m_data.heap :{. = ALIGN(8);__end__ = .;PROVIDE(end = .);__HeapBase = .;. += HEAP_SIZE;__HeapLimit = .;__heap_limit = .; /* Add for _sbrk */} > m_data.stack :{. = ALIGN(8);. += STACK_SIZE;} > m_data/* Initializes stack on the end of block */__StackTop = ORIGIN(m_data) + LENGTH(m_data);__StackLimit = __StackTop - STACK_SIZE;PROVIDE(__stack = __StackTop);.ARM.attributes 0 : { *(.ARM.attributes) }ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap")
}
从官方默认分散加载文件中,我们可以看到 text段(代码段),rodata段(只读段)运行域是在Flash中的,我们都知道rom的速度远远小于ram的,所以尽量将使用频繁的代码和变量拷贝到ram中运行。修改如下(展示部分):
/* Specify the memory areas */MEMORY{m_flash_config (RX) : ORIGIN = 0x30000400, LENGTH = 0x00000C00m_ivt (RX) : ORIGIN = 0x30001000, LENGTH = 0x00001000m_interrupts (RX) : ORIGIN = 0x30002000, LENGTH = 0x00000400m_rom_info (RX) : ORIGIN = 0x30002400, LENGTH = 0x00000400 /*用户信息,产品型号,版本等*/m_text (RX) : ORIGIN = 0x30002800, LENGTH = 0x007fdc00m_ram_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000400m_itcm_text (XRW) : ORIGIN = 0x00000400, LENGTH = 0x00037c00m_data (XRW) : ORIGIN = 0x20000000, LENGTH = 0x00048000m_ocram1 (XRW) : ORIGIN = 0x20240000, LENGTH = 0x00080000m_ocram2 (XRW) : ORIGIN = 0x202C0000, LENGTH = 0x00080000m_sdram (XRW) : ORIGIN = 0x80000000, LENGTH = 0x04000000}rom_info_begin = ORIGIN(m_rom_info);.rom_info : AT(rom_info_begin) {. = ALIGN(4);/*用户信息段*/KEEP(* (.rom_info)) . = ALIGN(4);} >m_rom_info/*程序代码和其他部分数据存放在.text段中*//* The program code and other data goes into internal flash */.text :{. = ALIGN(4);__TEXT_START = .; /*text段 起始地址*/*(.text) *(.reset) /*系统启动会先进这个函数,必须链接到ROM不能到RAM*/*(.text.SystemInit)*(.text.SystemInitHook)*crti.o(.text*)*crt0.o(.text*)*crtbegin.o(.text*)*crtend.o(.text*)*crtn.o(.text*)*(.glue_7) *(.glue_7t) *(.eh_frame)/* KEEP() 的作用是当启用连接器的--gc-sections垃圾回收选项时,这部分不能被回收 */KEEP (*(.init))KEEP (*(.fini)). = ALIGN(4);} > m_text.other_text :{. = ALIGN(4); _text_itcm_start_ = .;*(.other)*(.text.*) /* .rodata sections (constants, strings, etc.) 常量,字符串等*/*(.rodata) *(.rodata*) _text_itcm_end_ = .;. = ALIGN(4);} >m_itcm_text AT>m_text_text_load_start = LOADADDR(.other_text); /*加载地址*/
除此之外,还需要启动代码,将Reset_Handler和其他中断函数分为两个段(命名为reset和other段),分别链接到不同位置(ROM和RAM中)
/* Reset Handler */.section .reset, "x".thumb_func.align 2.globl Reset_Handler.weak Reset_Handler.type Reset_Handler, %function
Reset_Handler:cpsid i /* Mask interrupts */.equ VTOR, 0xE000ED08ldr r0, =VTORldr r1, =__isr_vectorstr r1, [r0]ldr r2, [r1]msr msp, r2.......pool.size Reset_Handler, . - Reset_Handler/*下面为 other段*/.section .other, "x".align 1.thumb_func.weak DefaultISR.type DefaultISR, %function
DefaultISR:b DefaultISR.size DefaultISR, . - DefaultISR ......
这时候我们编译代码,然后查阅map会发现当前大部分代码段已经链接到itcm中了
这样就算完了吗?如果你是修改keil的链接脚本scf文件,那么做到这里直接烧录程序就行了,因为剩下的拷贝交给__main来完成,但是我们用的是gcc,拷贝代码需要自己来写。这段拷贝代码需要在main函数之前完成,我们可以在startup_MIMXRT1176_cm7.s启动代码中用汇编语言写拷贝函数,也可以在SystemInitHook钩子函数中用C语言写拷贝函数。这里就拿代码段拷贝到itcm举例:
/*将代码段拷贝到 itcm*/ldr r1, =_text_load_startldr r2, =_text_itcm_start_ldr r3, =_text_itcm_end_
.LC_text_copy_start:cmp r2, r3ittt ltldrlt r0, [r1], #4strlt r0, [r2], #4blt .LC_text_copy_start
void SystemInitHook (void)
{ uint32_t startAddr; /* Address of the source memory. */uint32_t endAddr; /* End of copied memory. */uint32_t destAddr; //加载地址(flash)/*******************text拷贝************************/extern uint32_t _text_itcm_start_;extern uint32_t _text_itcm_end_;extern uint32_t _text_load_start;startAddr = (uint32_t)&_text_itcm_start_;endAddr = (uint32_t)&_text_itcm_end_;destAddr = (uint32_t)&_text_load_start;while(startAddr < endAddr){/* Copy one byte. */*((uint8_t *)startAddr) = *((uint8_t *)destAddr) ;/* Increment the destination and source pointers. */destAddr++;startAddr++;}
}