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 = 0x00000C00
m_ivt (RX) : ORIGIN = 0x30001000, LENGTH = 0x00001000
m_interrupts (RX) : ORIGIN = 0x30002000, LENGTH = 0x00000400
m_text (RX) : ORIGIN = 0x30002400, LENGTH = TEXT_SIZE
m_qacode (RX) : ORIGIN = 0x00000000, LENGTH = 0x00040000
m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00040000
m_data2 (RW) : ORIGIN = 0x202C0000 + RPMSG_SHMEM_SIZE, LENGTH = 0x00080000 - RPMSG_SHMEM_SIZE
rpmsg_sh_mem (RW) : ORIGIN = 0x202C0000, LENGTH = RPMSG_SHMEM_SIZE
m_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_config
ivt_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 of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end 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 = 0x00000C00
m_ivt (RX) : ORIGIN = 0x30001000, LENGTH = 0x00001000
m_interrupts (RX) : ORIGIN = 0x30002000, LENGTH = 0x00000400
m_rom_info (RX) : ORIGIN = 0x30002400, LENGTH = 0x00000400 /*用户信息,产品型号,版本等*/
m_text (RX) : ORIGIN = 0x30002800, LENGTH = 0x007fdc00
m_ram_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000400
m_itcm_text (XRW) : ORIGIN = 0x00000400, LENGTH = 0x00037c00
m_data (XRW) : ORIGIN = 0x20000000, LENGTH = 0x00048000
m_ocram1 (XRW) : ORIGIN = 0x20240000, LENGTH = 0x00080000
m_ocram2 (XRW) : ORIGIN = 0x202C0000, LENGTH = 0x00080000
m_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, 0xE000ED08
ldr r0, =VTOR
ldr r1, =__isr_vector
str 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_start
ldr r2, =_text_itcm_start_
ldr r3, =_text_itcm_end_
.LC_text_copy_start:
cmp r2, r3
ittt lt
ldrlt r0, [r1], #4
strlt r0, [r2], #4
blt .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++;
}
}